Compare commits

..

99 Commits

Author SHA1 Message Date
(no author)
758075df86 This commit was manufactured by cvs2svn to create tag 'CacheMergeWithTrunk'.
git-svn-id: svn://10.0.0.236/tags/CacheMergeWithTrunk@55505 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-07 01:40:46 +00:00
fur%netscape.com
76159a2caf Updated to NPL 1.1
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55504 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-07 01:40:45 +00:00
fur%netscape.com
1988cb05b5 Tweak comments
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55499 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-07 01:14:55 +00:00
fur%netscape.com
dd8c6b3bed Added nsINetDataCacheManager::SetDiskCacheFolder()
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55484 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-07 00:02:58 +00:00
fur%netscape.com
bc64749366 Account for arg changes in NewChannel() API
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55475 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:30:30 +00:00
fur%netscape.com
2e9287bd58 Match NewChannel() API changes on trunk
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55472 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:23:07 +00:00
fur%netscape.com
0f2274a140 Checkpoint
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55471 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:22:37 +00:00
fur%netscape.com
957fa10cac Retire nsINetDataCache::GetCapacity
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55469 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:16:10 +00:00
fur%netscape.com
e603146886 Track trunk API changes
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55467 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:15:32 +00:00
fur%netscape.com
cb698a0df1 + Retired nsINetDataCache::GetCapacity()
+ Fixed gcc build problem


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55466 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:14:38 +00:00
fur%netscape.com
1b918d9ef0 + Added proxy channel arg to NewChannel()
+ Changed name of setProtocolPrivate/getProtocolPrivate to setAnnotation/getAnnotation\
+ Added inUse attribute
+ Touched up comments


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55465 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:08:08 +00:00
fur%netscape.com
6407144c9f Removed capacity attribute
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55464 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:06:44 +00:00
fur%netscape.com
2ed563a178 Merge with trunk makefile.win
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@55462 18797224-902f-48f8-a5cc-f745e15eee43
1999-12-06 23:05:39 +00:00
fur%netscape.com
4becc0b508 Add assertion to cache manager to ensure that it is limiting cache occupancy
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54566 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-29 15:23:34 +00:00
fur%netscape.com
7d5427ea31 Obey the MAX_CONTENT_LENGTH limit
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54565 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-29 15:22:56 +00:00
fur%netscape.com
99904bcc48 Eliminate libs build target, as Warren has done for the rest of the tree
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54545 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-29 02:15:04 +00:00
fur%netscape.com
bcb56c9593 Add strong ref to channel
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54544 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-29 02:14:17 +00:00
fur%netscape.com
f18bc8e6cd Fix ownership issues. Change SetProtocolData/GetProtocolData args
to match new prototype.


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54109 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 07:51:56 +00:00
fur%netscape.com
7d14c5669a Handle NULL load group - they're supposed to be optional
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54108 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 07:51:14 +00:00
fur%netscape.com
c0dd3df02e Fix tons of ref-counting ownership issues and other bug fixes
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54107 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 07:50:25 +00:00
fur%netscape.com
e5cc84978f Checkpoint
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54097 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 05:08:20 +00:00
fur%netscape.com
5694537330 Changed SetProtocolData/GetProtocolData to accept a tag argument so that
multiple cache clients can attach info to the cache database.


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54096 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 05:07:40 +00:00
fur%netscape.com
297c5ceba3 Add/modify APIs to track nsIChannel
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54095 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 05:05:01 +00:00
fur%netscape.com
e0311312f7 Temporarily disable pref-reading code, since it doesn't work in the browser
and the code that measures the size of the cache db, since it's a performance
hog.


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54094 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 05:04:13 +00:00
fur%netscape.com
cf3dc77b02 Fix unitialized variable
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@54093 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 05:01:03 +00:00
fur%netscape.com
dd2506a737 Quash warnings
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53842 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-18 06:19:51 +00:00
fur%netscape.com
b1bee1f21c Merge with trunk
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53831 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-18 05:38:26 +00:00
fur%netscape.com
48ecc5625b Added review comments
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53674 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-16 19:46:28 +00:00
fur%netscape.com
1b791685e6 No longer need factory code. Its been moved to netwerk/cache/builds/nsNetDataCacheModule.cpp
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53663 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-16 18:21:11 +00:00
fur%netscape.com
dc94d1d6e2 Added review comments
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53647 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-16 10:08:26 +00:00
hoa.nguyen%intel.com
f5b437ade3 added Unix support
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53574 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-16 00:21:05 +00:00
hoa.nguyen%intel.com
8c0b4b3e4a changed NPL to MPL
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53573 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-16 00:16:33 +00:00
hoa.nguyen%intel.com
fb640ab144 added Truncate function
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53572 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-16 00:07:27 +00:00
hoa.nguyen%intel.com
106e263b33 added support for memory cache
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53570 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-16 00:00:54 +00:00
fur%netscape.com
85045a8552 Add TestCacheMgr
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53537 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 21:13:19 +00:00
fur%netscape.com
a2279be132 *** empty log message ***
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53536 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 21:12:58 +00:00
fur%netscape.com
0ce702d402 Don't call NS_ERROR() when a record ID is not found
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53533 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 21:12:17 +00:00
fur%netscape.com
f14d03cd67 Fixed CommitFlags()
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53531 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 21:11:14 +00:00
fur%netscape.com
b91343fdf6 Checkpoint
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53520 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 18:30:44 +00:00
fur%netscape.com
2c517489b5 Disable warning, so cache code can run
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53519 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 17:39:35 +00:00
(no author)
ae57da58eb This commit was manufactured by cvs2svn to create branch
'CacheIntegration_BRANCH'.

git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53517 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 17:10:07 +00:00
fur%netscape.com
8d4586dd65 Update components table and macro instantiations to conform to new definitions
in nsIGenericFactory.h


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53499 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 09:06:41 +00:00
fur%netscape.com
9b473ad9be Added starting offset param to interceptAsyncRead() method
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53498 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 08:53:15 +00:00
fur%netscape.com
2ad227a994 Merged with trunk
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53496 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 08:01:58 +00:00
fur%netscape.com
94d8da33c1 Replace 1.0 NPL with 1.1 version
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53487 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 06:13:13 +00:00
fur%netscape.com
81c05809fd Remove dead files
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53486 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 06:08:40 +00:00
fur%netscape.com
e30547b2b2 Remove dead files
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53485 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 05:52:29 +00:00
fur%netscape.com
257f9cfaaa Fix Boogs. Replace 1.0 NPL with 1.1 version
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53484 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-15 05:51:02 +00:00
fur%netscape.com
96e2654e43 Replace 1.0 NPL with 1.1 NPL
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53474 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-14 20:35:26 +00:00
fur%netscape.com
3b023433be Replace 1.0 NPL with 1.1 version
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53472 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-14 19:56:44 +00:00
fur%netscape.com
1b89716afe Added more comments
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53471 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-14 19:50:30 +00:00
fur%netscape.com
ad02058877 Add comments. Change method names
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53470 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-14 18:51:57 +00:00
fur%netscape.com
fa8a3196e7 Merge with trunk
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53434 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-13 19:35:49 +00:00
fur%netscape.com
5c2c543e58 Fixed bugs which prevented embedded NUL characters
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53431 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-13 18:45:28 +00:00
fur%netscape.com
c588721cc0 Added NS_NewStorageStream().
Changed method name, Initialize ==> Init


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53430 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-13 18:43:20 +00:00
fur%netscape.com
0b049b17ba Fix Boogs
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53429 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-13 18:41:01 +00:00
fur%netscape.com
854ef4631d Merge from trunk
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53268 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-11 22:29:20 +00:00
fur%netscape.com
051c558653 Detect failure to truncate cache entry
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53214 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-11 18:16:24 +00:00
fur%netscape.com
37a04adb09 Killed build warnings. Added stubs for unimplemented methods
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53072 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-10 06:05:33 +00:00
fur%netscape.com
c823c04b45 Combine cache components into module
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@53014 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-09 22:08:49 +00:00
fur%netscape.com
03cbd000eb Sync with trunk
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52998 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-09 17:36:48 +00:00
fur%netscape.com
a91e91a1c7 Added Windows makefiles so that the cache manager, file cache and
memory cache components are built as part of netlib and combined into
a single XPCOM module, named "nkcache.dll"


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52970 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:44:04 +00:00
fur%netscape.com
be7a5a48b6 Added call to LimitCacheSize
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52968 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:40:41 +00:00
fur%netscape.com
b2bc7468e8 Add cache manager CID
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52967 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:38:20 +00:00
fur%netscape.com
9692dfd994 Add cache manager ProgID
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52966 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:37:30 +00:00
fur%netscape.com
f3edd4cfb5 Added an owning reference from nsDiskCacheRecordChannel to
its associated nsDiskCacheRecord.  Without this, the channel
may access free'ed memory.


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52962 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:28:39 +00:00
fur%netscape.com
33403345c1 Rename class to avoid name collision with similar code in file cache.
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52961 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:25:28 +00:00
fur%netscape.com
20c850de23 Merge with tip
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52960 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:23:35 +00:00
fur%netscape.com
e8b619cd02 Merge with tip
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52959 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 22:18:25 +00:00
fur%netscape.com
eed396bb92 Stabilize ref-count during construction
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52946 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-08 18:46:17 +00:00
fur%netscape.com
41d44c070b Eliminate dead files
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52932 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-07 20:19:55 +00:00
fur%netscape.com
698ba42268 Revamped directory structure
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52922 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-06 19:36:16 +00:00
(no author)
1a0fd23991 This commit was manufactured by cvs2svn to create branch
'CacheIntegration_BRANCH'.

git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52912 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-06 03:43:56 +00:00
fur%netscape.com
67dded330b Add nsDiskCacheRecordChannel.cpp
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52876 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 22:18:26 +00:00
fur%netscape.com
936ff4777a Fix compilation errors on Win32
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52874 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 22:18:04 +00:00
fur%netscape.com
96c55e42f7 Accommodate API changes in nsINetDataCache
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52873 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 22:17:42 +00:00
fur%netscape.com
82fa0cf06a Got rid of GetReadOnly(). Added GetFlags()
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52872 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 22:15:05 +00:00
fur%netscape.com
98c8285334 First shot at Win32 makefile
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52869 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 22:03:02 +00:00
fur%netscape.com
bad4b683f4 Removed SetCapacity() method
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52868 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 22:01:26 +00:00
fur%netscape.com
cb5269a28a Checkpoint
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52864 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 21:47:25 +00:00
fur%netscape.com
74712f3635 Added binary I/O streams
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52860 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 20:02:06 +00:00
fur%netscape.com
9f8ea739db Correct error comment
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52859 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-05 20:01:39 +00:00
hoa.nguyen%intel.com
97a10dd7c6 Add offset writing for nsOutputStream
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52789 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-04 18:02:39 +00:00
hoa.nguyen%intel.com
85a132fac0 Add command line switch to test memory and disk cache.
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52788 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-04 18:01:28 +00:00
hoa.nguyen%intel.com
e594eee877 Add proxy channel interface, and misc bug fixes.
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@52787 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-04 17:59:42 +00:00
hoa.nguyen%intel.com
9148eee3d6 Initial checkin of disk cache modules
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@51580 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-22 22:37:18 +00:00
hoa.nguyen%intel.com
b5989a8382 Initial checkin of disk cache module
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@51579 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-22 22:36:07 +00:00
fur%netscape.com
2b861f60d9 Create a new channel for every call to Write()
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@51335 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-21 01:35:51 +00:00
fur%netscape.com
87db050b37 Added tests for:
nsINetDataCache::GetStorageInUse()
    nsINetDataCacheRecord::SetContentLength()
    nsIOutputStream::Write(), using non-zero starting offsets


git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@51071 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-18 23:55:44 +00:00
fur%netscape.com
ffe483cf95 Initial cut at memory-cache functionality is complete
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@51067 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-18 23:46:08 +00:00
fur%netscape.com
52aa17a1c3 Incorporate nsStorageStream into xpcom.dll
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50814 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:40:42 +00:00
fur%netscape.com
5fdb3aa69e Initial implementation of 'storage stream' - used as the heart of the memory cache
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50812 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:39:45 +00:00
fur%netscape.com
8cae473bc0 Add opaque keys to nsHashtable
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50811 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:38:01 +00:00
fur%netscape.com
0719303755 Fix linkage problem
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50810 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:37:21 +00:00
fur%netscape.com
90d3e40858 Fix bugs in Next(). Prev() and IsDone()
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50809 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:36:35 +00:00
fur%netscape.com
c792b2d35c Changed IDL to generate identical C++ headers, but with better scriptability
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50808 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:32:04 +00:00
fur%netscape.com
7a4377d840 Initial cut at memory cache
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50806 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:24:06 +00:00
fur%netscape.com
a5fa416010 Added TestRawCache.cpp
git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50804 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-15 07:19:52 +00:00
(no author)
49d00db5e2 This commit was manufactured by cvs2svn to create branch
'CacheIntegration_BRANCH'.

git-svn-id: svn://10.0.0.236/branches/CacheIntegration_BRANCH@50589 18797224-902f-48f8-a5cc-f745e15eee43
1999-10-13 10:24:13 +00:00
285 changed files with 9037 additions and 43583 deletions

38
mozilla/netwerk/cache/Makefile.in vendored Normal file
View File

@@ -0,0 +1,38 @@
#
# The contents of this file are subject to the Netscape 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/NPL/
#
# 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 mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = \
public \
memcache \
filecache \
mgr \
build \
$(NULL)
include $(topsrcdir)/config/rules.mk

33
mozilla/netwerk/cache/Makefile.win vendored Executable file
View File

@@ -0,0 +1,33 @@
#!gmake
#
# The contents of this file are subject to the Netscape 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/NPL/
#
# 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 mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
DEPTH=..\..
DIRS= \
public \
mgr \
memcache \
filecache \
build \
$(NULL)
include <$(DEPTH)\config\rules.mak>

54
mozilla/netwerk/cache/build/Makefile.in vendored Normal file
View File

@@ -0,0 +1,54 @@
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkcache
IS_COMPONENT = 1
CPPSRCS = nsNetDataCacheModule.cpp
SHARED_LIBRARY_LIBS = \
$(DIST)/lib/libnkcachemgr_s.a \
$(DIST)/lib/libnkfilecache_s.a \
$(DIST)/lib/libnkmemcache_s.a \
$(DIST)/lib/libmozdbm_s.a \
$(DIST)/lib/libxpcomio_s.a \
$(NULL)
LOCAL_INCLUDES = \
-I$(DEPTH)/netwerk/cache/memcache \
-I$(DEPTH)/netwerk/cache/filecache \
-I$(DEPTH)/netwerk/cache/mgr \
$(NULL)
EXTRA_DSO_LDOPTS = \
$(MKSHLIB_FORCE_ALL) \
$(SHARED_LIBRARY_LIBS) \
$(MKSHLIB_UNFORCE_ALL) \
$(NULL)
include $(topsrcdir)/config/rules.mk
$(LIBRARY) $(SHARED_LIBRARY): $(SHARED_LIBRARY_LIBS) Makefile

View File

@@ -0,0 +1,51 @@
#!gmake
#
# The contents of this file are subject to the Netscape 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/NPL/
#
# 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 mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
DEPTH=..\..\..
MODULE=nkcache
MAKE_OBJ_TYPE=DLL
DLLNAME=nkcache
DLL=.\$(OBJDIR)\$(DLLNAME).dll
CPP_OBJS= \
.\$(OBJDIR)\nsNetDataCacheModule.obj \
$(NULL)
LLIBS= \
$(DIST)\lib\nkcachemgr_s.lib \
$(DIST)\lib\nkfilecache_s.lib \
$(DIST)\lib\nkmemcache_s.lib \
$(DIST)\lib\dbm32.lib \
$(DIST)\lib\xpcom.lib \
$(LIBNSPR)
INCS = $(INCS) \
-I$(DEPTH)\netwerk\cache\memcache \
-I$(DEPTH)\netwerk\cache\filecache \
-I$(DEPTH)\netwerk\cache\mgr \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(DLL)
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).dll $(DIST)\bin\components
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).lib $(DIST)\lib

View File

@@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsCOMPtr.h"
#include "nsIModule.h"
#include "nscore.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIGenericFactory.h"
#include "nsINetDataCache.h"
#include "nsINetDataCacheManager.h"
#include "nsMemCacheCID.h"
#include "nsMemCache.h"
#include "nsNetDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsCacheManager.h"
// Factory method to create a new nsMemCache instance. Used
// by nsNetDataCacheModule
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemCache, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNetDiskCache, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCacheManager, Init)
static nsModuleComponentInfo components[] = {
{ "Memory Cache", NS_MEM_CACHE_FACTORY_CID, NS_NETWORK_MEMORY_CACHE_PROGID, nsMemCacheConstructor },
{ "File Cache", NS_NETDISKCACHE_CID, NS_NETWORK_FILE_CACHE_PROGID, nsNetDiskCacheConstructor },
{ "Cache Manager",NS_CACHE_MANAGER_CID, NS_NETWORK_CACHE_MANAGER_PROGID,nsCacheManagerConstructor }
};
NS_IMPL_NSGETMODULE("Network Data Cache", components)

View File

@@ -0,0 +1,56 @@
#
# 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 Mozilla Communicator.
#
# The Initial Developer of the Original Code is Intel Corp.
# Portions created by Intel Corp. are
# Copyright (C) 1999, 1999 Intel Corp. All
# Rights Reserved.
#
# Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
# Carl Wong <carl.wong@intel.com>
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
VPATH = @srcdir@
srcdir = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkfilecache_s
REQUIRES = nspr dbm
EXTRA_DSO_LDOPTS += -L$(DIST)/lib -lmozdbm_s
EXPORTS=nsNetDiskCacheCID.h
CPPSRCS = \
nsDBAccessor.cpp\
nsDBEnumerator.cpp \
nsNetDiskCache.cpp \
nsDiskCacheRecord.cpp \
nsDiskCacheRecordChannel.cpp \
$(NULL)
EXTRA_LIBS = $(NSPR_LIBS)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,44 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkfilecache_s
CPP_OBJS= \
.\$(OBJDIR)\nsDBAccessor.obj \
.\$(OBJDIR)\nsDBEnumerator.obj \
.\$(OBJDIR)\nsNetDiskCache.obj \
.\$(OBJDIR)\nsDiskCacheRecord.obj \
.\$(OBJDIR)\nsDiskCacheRecordChannel.obj \
$(NULL)
EXPORTS=nsNetDiskCacheCID.h
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -0,0 +1,351 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#include "nsDBAccessor.h"
#include "nscore.h"
#include "prtypes.h"
#include "plhash.h"
#include "nsCRT.h"
#include "nsAutoLock.h"
nsDBAccessor::nsDBAccessor() :
mDB(0) ,
mSessionID(0) ,
mSessionCntr(0)
{
NS_INIT_REFCNT();
}
nsDBAccessor::~nsDBAccessor()
{
printf(" ~nsDBAccessor\n") ;
Shutdown() ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDBAccessor, NS_GET_IID(nsIDBAccessor))
///////////////////////////////////////////////////////////
// nsIDBAccessor methods
NS_IMETHODIMP
nsDBAccessor::Init(nsIFileSpec* dbfile)
{
// FUR - lock not needed
m_Lock = PR_NewLock() ;
if(!m_Lock)
return NS_ERROR_OUT_OF_MEMORY ;
char* dbname ;
// this should cover all platforms.
dbfile->GetNativePath(&dbname) ;
// FUR - how is page size chosen ? It's worth putting a comment
// in here about the possible usefulness of tuning these parameters
HASHINFO hash_info = {
16*1024 , /* bucket size */
0 , /* fill factor */
0 , /* number of elements */
0 , /* bytes to cache */
0 , /* hash function */
0} ; /* byte order */
// FUR - lock not needed
nsAutoLock lock(m_Lock) ;
mDB = dbopen(dbname,
O_RDWR | O_CREAT ,
0600 ,
DB_HASH ,
& hash_info) ;
// FUR - does dbname have to be free'ed ?
if(!mDB)
return NS_ERROR_FAILURE ;
// set mSessionID
// FUR - Why the +1 ? (No need for key to be NUL-terminated string.)
PRUint32 len = PL_strlen(SessionKey)+1 ;
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, SessionKey) ;
db_key.size = len ;
int status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == -1) {
NS_ERROR("ERROR: failed get session id in database.") ;
return NS_ERROR_FAILURE ;
}
if(status == 0) {
// get the last session id
PRInt16 *old_ID = NS_STATIC_CAST(PRInt16*, db_data.data) ;
if(*old_ID < ini_sessionID) {
NS_ERROR("ERROR: Bad Session ID in database, corrupted db.") ;
return NS_ERROR_FAILURE ;
}
// FUR - need to comment out all printfs, or turn them into PR_LOG statements
printf("found previous session, id = %d\n", *old_ID) ;
mSessionID = *old_ID + 1 ;
}
else if(status == 1) {
// must be a new db
mSessionID = ini_sessionID ;
}
db_data.data = NS_REINTERPRET_CAST(void*, &mSessionID) ;
db_data.size = sizeof(PRInt16) ;
// store the new session id
status = (*mDB->put)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
(*mDB->sync)(mDB, 0) ;
return NS_OK ;
}
else {
NS_ERROR("reset session ID failure.") ;
return NS_ERROR_FAILURE ;
}
}
NS_IMETHODIMP
nsDBAccessor::Shutdown(void)
{
if(mDB) {
(*mDB->sync)(mDB, 0) ;
(*mDB->close)(mDB) ;
mDB = nsnull ;
}
// FUR - locks not necessary
if(m_Lock)
PR_DestroyLock(m_Lock);
return NS_OK ;
}
NS_IMETHODIMP
nsDBAccessor::Get(PRInt32 aID, void** anEntry, PRUint32 *aLength)
{
if(!anEntry)
return NS_ERROR_NULL_POINTER ;
*anEntry = nsnull ;
*aLength = 0 ;
NS_ASSERTION(mDB, "no database") ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
int status = 0 ;
status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
*anEntry = db_data.data ;
*aLength = db_data.size ;
return NS_OK ;
}
else if(status == 1)
return NS_OK ;
else
return NS_ERROR_FAILURE ;
}
NS_IMETHODIMP
nsDBAccessor::Put(PRInt32 aID, void* anEntry, PRUint32 aLength)
{
NS_ASSERTION(mDB, "no database") ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
db_data.data = anEntry ;
db_data.size = aLength ;
if(0 == (*mDB->put)(mDB, &db_key, &db_data, 0)) {
// FUR - I would avoid unnecessary sync'ing for performance's
// sake. Maybe you could limit sync to max rate of, say, once
// every few seconds by keeping track of last sync time, using PR_Now().
(*mDB->sync)(mDB, 0) ;
return NS_OK ;
}
else {
// FUR - Try to avoid using NS_ERROR unless error is unrecoverable and serious
NS_ERROR("ERROR: Failed to put anEntry into db.\n") ;
return NS_ERROR_FAILURE ;
}
}
/*
* It's more important to remove the id->metadata entry first since
* key->id mapping is just a reference
*/
NS_IMETHODIMP
nsDBAccessor::Del(PRInt32 aID, void* anEntry, PRUint32 aLength)
{
NS_ASSERTION(mDB, "no database") ;
// FUR - no locks necessary
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key ;
// delete recordID->metadata
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
PRInt32 status = -1 ;
status = (*mDB->del)(mDB, &db_key, 0) ;
if(-1 == status) {
// FUR - no printf's, use PR_LOG, NS_WARNING, or NS_ASSERTION, as the situation warrants
printf(" delete error\n") ;
return NS_ERROR_FAILURE ;
}
// delete key->recordID
db_key.data = anEntry ;
db_key.size = aLength ;
status = (*mDB->del)(mDB, &db_key, 0) ;
if(-1 == status) {
// FUR - no printf's
printf(" delete error\n") ;
return NS_ERROR_FAILURE ;
}
// FUR - Defer sync ? See above
(*mDB->sync)(mDB, 0) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDBAccessor::GetID(const char* key, PRUint32 length, PRInt32* aID)
{
NS_ASSERTION(mDB, "no database") ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, key) ;
db_key.size = length ;
int status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
// found recordID
*aID = *(NS_REINTERPRET_CAST(PRInt32*, db_data.data)) ;
return NS_OK ;
}
else if(status == 1) {
// create a new one
PRInt32 id = 0 ;
id = mSessionID << 16 | mSessionCntr++ ;
// add new id into mDB
db_data.data = NS_REINTERPRET_CAST(void*, &id) ;
db_data.size = sizeof(PRInt32) ;
status = (*mDB->put)(mDB, &db_key, &db_data, 0) ;
if(status != 0) {
NS_ERROR("updating db failure.") ;
return NS_ERROR_FAILURE ;
}
// FUR - defer sync ?
(*mDB->sync)(mDB, 0) ;
*aID = id ;
return NS_OK ;
}
else {
NS_ERROR("ERROR: keydb failure.") ;
return NS_ERROR_FAILURE ;
}
}
NS_IMETHODIMP
nsDBAccessor::EnumEntry(void** anEntry, PRUint32* aLength, PRBool bReset)
{
if(!anEntry)
return NS_ERROR_NULL_POINTER ;
*anEntry = nsnull ;
*aLength = 0 ;
NS_ASSERTION(mDB, "no database") ;
PRUint32 flag ;
if(bReset)
flag = R_FIRST ;
else
flag = R_NEXT ;
// Lock the db
nsAutoLock lock(m_Lock) ;
DBT db_key, db_data ;
// FUR - +1 unnecessary ?
PRUint32 len = PL_strlen(SessionKey)+1 ;
int status ;
do {
status = (*mDB->seq)(mDB, &db_key, &db_data, flag) ;
flag = R_NEXT ;
if(status == -1)
return NS_ERROR_FAILURE ;
// get next if it's a key->recordID
if(db_key.size > sizeof(PRInt32) && db_data.size == sizeof(PRInt32))
continue ;
// get next if it's a sessionID entry
if(db_key.size == len && db_data.size == sizeof(PRInt16))
continue ;
// recordID is always 32 bits long
if(db_key.size == sizeof(PRInt32))
break ;
} while(!status) ;
if (0 == status) {
*anEntry = db_data.data ;
*aLength = db_data.size ;
}
return NS_OK ;
}

View File

@@ -0,0 +1,68 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#ifndef _NSIDBACCESSOR_H_
#define _NSIDBACCESSOR_H_
#include "nsIDBAccessor.h"
#include "mcom_db.h"
// bogus string for the key of session id
// FUR - suggest "SK" instead of "^^"
static const char * const SessionKey = "^^" ;
// initial session id number
static const PRInt16 ini_sessionID = 0xff ;
class nsDBAccessor : public nsIDBAccessor
{
public:
NS_DECL_ISUPPORTS
nsDBAccessor() ;
virtual ~nsDBAccessor() ;
NS_IMETHOD Init(nsIFileSpec* dbfile) ;
NS_IMETHOD Shutdown(void) ;
NS_IMETHOD Put(PRInt32 aID, void* anEntry, PRUint32 aLength) ;
NS_IMETHOD Get(PRInt32 aID, void** anEntry, PRUint32 *aLength) ;
NS_IMETHOD Del(PRInt32 aID, void* anEntry, PRUint32 aLength) ;
NS_IMETHOD GetID(const char* key, PRUint32 length, PRInt32* aID) ;
NS_IMETHOD EnumEntry(void* *anEntry, PRUint32* aLength, PRBool bReset) ;
protected:
private:
DB * mDB ;
PRInt16 mSessionID ;
PRInt16 mSessionCntr ;
PRLock * m_Lock ;
} ;
#endif // _NSIDBACCESSOR_H_

View File

@@ -0,0 +1,97 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#include "nsDBEnumerator.h"
#include "nsDiskCacheRecord.h"
nsDBEnumerator::nsDBEnumerator(nsIDBAccessor* aDB, nsNetDiskCache* aCache) :
m_DB(aDB) ,
m_DiskCache(aCache) ,
tempEntry(0) ,
tempEntry_length(0) ,
m_CacheEntry(0) ,
bReset(PR_TRUE)
{
NS_INIT_REFCNT();
}
nsDBEnumerator::~nsDBEnumerator()
{
// printf(" ~nsDBEnumerator()\n") ;
NS_IF_RELEASE(m_CacheEntry) ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDBEnumerator, NS_GET_IID(nsIEnumerator))
/////////////////////////////////////////////////////////////////
// nsISimpleEnumerator methods
NS_IMETHODIMP
nsDBEnumerator::HasMoreElements(PRBool *_retval)
{
*_retval = PR_FALSE ;
m_DB->EnumEntry(&tempEntry, &tempEntry_length, bReset) ;
bReset = PR_FALSE ;
if(tempEntry && tempEntry_length != 0)
*_retval = PR_TRUE ;
return NS_OK ;
}
// this routine does not create a new item by itself
// Rather it reuses the item inside the object. So if you need to use the
// item later, you have to
// create a new item specifically, using copy constructor or some other dup
// function. And don't forget to release it after you're done
//
NS_IMETHODIMP
nsDBEnumerator::GetNext(nsISupports **_retval)
{
if(!m_CacheEntry) {
m_CacheEntry = new nsDiskCacheRecord(m_DB, m_DiskCache) ;
if(m_CacheEntry)
NS_ADDREF(m_CacheEntry) ;
else
return NS_ERROR_OUT_OF_MEMORY ;
}
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsresult rv = m_CacheEntry->RetrieveInfo(tempEntry, tempEntry_length) ;
if(NS_FAILED(rv))
return rv ;
*_retval = NS_STATIC_CAST(nsISupports*, m_CacheEntry) ;
NS_ADDREF(*_retval) ; // all good getter addref
return NS_OK ;
}

View File

@@ -0,0 +1,61 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#ifndef _NS_DBENUMERATOR_H_
#define _NS_DBENUMERATOR_H_
#include "nsISimpleEnumerator.h"
#include "nsINetDataCacheRecord.h"
#include "nsIDBAccessor.h"
#include "nsCOMPtr.h"
#include "nsNetDiskCache.h"
#include "nsDiskCacheRecord.h"
class nsCachedDiskData ; /* forward decl */
class nsDBEnumerator : public nsISimpleEnumerator {
public:
NS_DECL_ISUPPORTS
// FUR can use NS_DECL_NSISIMPLEENUMERATOR here
/* boolean HasMoreElements (); */
NS_IMETHOD HasMoreElements(PRBool *_retval) ;
/* nsISupports GetNext (); */
NS_IMETHOD GetNext(nsISupports **_retval) ;
nsDBEnumerator(nsIDBAccessor* aDB, nsNetDiskCache* aCache) ;
virtual ~nsDBEnumerator() ;
// FUR all members should be prefixed by 'm', e.g. mbReset
private:
nsCOMPtr<nsIDBAccessor> m_DB ;
nsCOMPtr<nsNetDiskCache> m_DiskCache ;
void * tempEntry ;
PRUint32 tempEntry_length ;
nsDiskCacheRecord* m_CacheEntry ;
PRBool bReset ;
};
#endif // _NS_DBENUMERATOR_H_

View File

@@ -0,0 +1,451 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsDiskCacheRecord.h"
#include "nsINetDataDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsDiskCacheRecordChannel.h"
#include "nsFileStream.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIProtocolHandler.h"
#include "nsIIOService.h"
#include "nsIAllocator.h"
#include "plstr.h"
#include "prprf.h"
#include "prmem.h"
#include "prlog.h"
#include "prtypes.h"
#include "netCore.h"
#include "nsDBAccessor.h"
#if !defined(IS_LITTLE_ENDIAN) && !defined(IS_BIG_ENDIAN)
ERROR! Must have a byte order
#endif
#ifdef IS_LITTLE_ENDIAN
#define COPY_INT32(_a,_b) memcpy(_a, _b, sizeof(int32))
#else
#define COPY_INT32(_a,_b) /* swap */ \
do { \
((char *)(_a))[0] = ((char *)(_b))[3]; \
((char *)(_a))[1] = ((char *)(_b))[2]; \
((char *)(_a))[2] = ((char *)(_b))[1]; \
((char *)(_a))[3] = ((char *)(_b))[0]; \
} while(0)
#endif
nsDiskCacheRecord::nsDiskCacheRecord(nsIDBAccessor* db, nsNetDiskCache* aCache) :
mKey(0) ,
mKeyLength(0) ,
mRecordID(0) ,
mMetaData(0) ,
mMetaDataLength(0) ,
mDB(db) ,
mInfo(0) ,
mInfoSize(0) ,
mNumChannels(0) ,
mDiskCache(aCache)
{
NS_INIT_REFCNT();
}
// mem alloced. so caller should do free() on key.
NS_IMETHODIMP
nsDiskCacheRecord::Init(const char* key, PRUint32 length)
{
NS_NewFileSpec(getter_AddRefs(mFile));
if(!mFile)
return NS_ERROR_OUT_OF_MEMORY ;
// copy key
mKeyLength = length ;
mKey = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, key, length) ;
// get RecordID
// FUR!! Another disk access ? If called from GetCachedData, ID is already known
mDB->GetID(key, length, &mRecordID) ;
// FUR - check for GetID failure
// setup the file name
nsCOMPtr<nsIFileSpec> dbFolder ;
mDiskCache->GetDiskCacheFolder(getter_AddRefs(dbFolder)) ;
nsresult rv = mFile->FromFileSpec(dbFolder) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
// dir is a hash result of mRecordID%32, hope it's enough
char filename[9], dirName[3] ;
// FUR!! - should the format string be "%.02x". How does this work !?
PR_snprintf(dirName, 3, "%.2x", (((PRUint32)mRecordID) % 32)) ;
mFile->AppendRelativeUnixPath(dirName) ;
// FUR!! - should the format string be "%.08x". How does this work !?
PR_snprintf(filename, 9, "%.8x", mRecordID) ;
mFile->AppendRelativeUnixPath(filename) ;
return NS_OK ;
}
nsDiskCacheRecord::~nsDiskCacheRecord()
{
// printf(" ~nsDiskCacheRecord()\n") ;
if(mKey)
nsAllocator::Free(mKey) ;
if(mMetaData)
nsAllocator::Free(mMetaData) ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDiskCacheRecord, NS_GET_IID(nsINetDataCacheRecord))
///////////////////////////////////////////////////////////////////////
// nsINetDataCacheRecord methods
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetKey(PRUint32 *length, char** _retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*length = mKeyLength ;
*_retval = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mKey, mKeyLength) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetRecordID(PRInt32* aRecordID)
{
*aRecordID = mRecordID ;
return NS_OK ;
}
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetMetaData(PRUint32 *length, char **_retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
// always null the return value first.
*_retval = nsnull ;
*length = mMetaDataLength ;
if(mMetaDataLength) {
*_retval = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mMetaData, mMetaDataLength) ;
}
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::SetMetaData(PRUint32 length, const char* data)
{
// set the mMetaData
mMetaDataLength = length ;
if(mMetaData)
nsAllocator::Free(mMetaData) ;
mMetaData = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData) {
return NS_ERROR_OUT_OF_MEMORY ;
}
memcpy(mMetaData, data, length) ;
// Generate mInfo
nsresult rv = GenInfo() ;
if(NS_FAILED(rv))
return rv ;
// write through into mDB
rv = mDB->Put(mRecordID, mInfo, mInfoSize) ;
// FUR - mInfo leaking ?
return rv ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetStoredContentLength(PRUint32 *aStoredContentLength)
{
return mFile->GetFileSize(aStoredContentLength) ;
}
// untill nsIFileSpec::Truncate() is in, we have to do all this ugly stuff
NS_IMETHODIMP
nsDiskCacheRecord::SetStoredContentLength(PRUint32 aStoredContentLength)
{
PRUint32 len = 0 ;
nsresult rv = mFile->GetFileSize(&len) ;
if(NS_FAILED(rv))
return rv ;
if(len < aStoredContentLength)
{
NS_ERROR("Error: can not set filesize to something bigger than itself.\n") ;
return NS_ERROR_FAILURE ;
}
else
return mFile->Truncate(aStoredContentLength) ;
}
NS_IMETHODIMP
nsDiskCacheRecord::Delete(void)
{
if(mNumChannels)
return NS_ERROR_NOT_AVAILABLE ;
PRUint32 len ;
mFile->GetFileSize(&len) ;
nsFileSpec cache_file ;
nsresult rv = mFile->GetFileSpec(&cache_file) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
cache_file.Delete(PR_TRUE) ;
// updata the storage size
mDiskCache->m_StorageInUse -= len ;
rv = mDB->Del(mRecordID, mKey, mKeyLength) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
else
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetFilename(nsIFileSpec * *aFilename)
{
if(!aFilename)
return NS_ERROR_NULL_POINTER ;
*aFilename = mFile ;
NS_ADDREF(*aFilename) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::NewChannel(nsILoadGroup *loadGroup, nsIChannel **_retval)
{
nsDiskCacheRecordChannel* channel = new nsDiskCacheRecordChannel(this, loadGroup) ;
if(!channel)
return NS_ERROR_OUT_OF_MEMORY ;
nsresult rv = channel->Init() ;
if(NS_FAILED(rv))
return rv ;
NS_ADDREF(channel) ;
*_retval = NS_STATIC_CAST(nsIChannel*, channel) ;
return NS_OK ;
}
//////////////////////////////////////////////////////////////////////////
// nsDiskCacheRecord methods
// file name is represented by a url string. I hope this would be more
// generic
nsresult
nsDiskCacheRecord::GenInfo()
{
if(mInfo)
nsAllocator::Free(mInfo) ;
char* file_url=nsnull ;
PRUint32 name_len ;
mFile->GetURLString(&file_url) ;
name_len = PL_strlen(file_url)+1 ;
mInfoSize = sizeof(PRUint32) ; // checksum for mInfoSize
mInfoSize += sizeof(PRInt32) ; // RecordID
mInfoSize += sizeof(PRUint32) ; // key length
mInfoSize += mKeyLength ; // key
mInfoSize += sizeof(PRUint32) ; // metadata length
mInfoSize += mMetaDataLength ; // metadata
mInfoSize += sizeof(PRUint32) ; // filename length
mInfoSize += name_len ; // filename
void* newInfo = nsAllocator::Alloc(mInfoSize*sizeof(char)) ;
if(!newInfo) {
return NS_ERROR_OUT_OF_MEMORY ;
}
// copy the checksum mInfoSize
char* cur_ptr = NS_STATIC_CAST(char*, newInfo) ;
COPY_INT32(cur_ptr, &mInfoSize) ;
cur_ptr += sizeof(PRUint32) ;
// copy RecordID
COPY_INT32(cur_ptr, &mRecordID) ;
cur_ptr += sizeof(PRInt32) ;
// copy key length
COPY_INT32(cur_ptr, &mKeyLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy key
memcpy(cur_ptr, mKey, mKeyLength) ;
cur_ptr += mKeyLength ;
// copy metadata length
COPY_INT32(cur_ptr, &mMetaDataLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy metadata
memcpy(cur_ptr, mMetaData, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// copy file name length
COPY_INT32(cur_ptr, &name_len) ;
cur_ptr += sizeof(PRUint32) ;
// copy file name
memcpy(cur_ptr, file_url, name_len) ;
cur_ptr += name_len ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, newInfo) + mInfoSize);
mInfo = newInfo ;
return NS_OK ;
}
/*
* This Method suppose to get all the info from the db record
* and set them to accroding members. the original values
* will all be overwritten. only minimal error checking is performed.
*/
NS_IMETHODIMP
nsDiskCacheRecord::RetrieveInfo(void* aInfo, PRUint32 aInfoLength)
{
// reset everything
if(mInfo) {
nsAllocator::Free(mInfo) ;
mInfo = nsnull ;
}
if(mKey) {
nsAllocator::Free(mKey) ;
mKey = nsnull ;
}
if(mMetaData) {
nsAllocator::Free(mMetaData) ;
mMetaData = nsnull ;
}
char * cur_ptr = NS_STATIC_CAST(char*, aInfo) ;
char* file_url ;
PRUint32 name_len ;
// set mInfoSize
COPY_INT32(&mInfoSize, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// check this at least
if(mInfoSize != aInfoLength)
return NS_ERROR_FAILURE ;
// set mRecordID
COPY_INT32(&mRecordID, cur_ptr) ;
cur_ptr += sizeof(PRInt32) ;
// set mKeyLength
COPY_INT32(&mKeyLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// set mKey
mKey = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, cur_ptr, mKeyLength) ;
cur_ptr += mKeyLength ;
PRInt32 id ;
mDB->GetID(mKey, mKeyLength, &id) ;
NS_ASSERTION(id==mRecordID, "\t ++++++ bad record, somethings wrong\n") ;
// set mMetaDataLength
COPY_INT32(&mMetaDataLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// set mMetaData
mMetaData = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mMetaData, cur_ptr, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// get mFile name length
COPY_INT32(&name_len, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// get mFile native name
file_url = NS_STATIC_CAST(char*, nsAllocator::Alloc(name_len*sizeof(char))) ;
if(!file_url)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(file_url, cur_ptr, name_len) ;
cur_ptr += name_len ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, aInfo) + mInfoSize);
// create mFile if Init() isn't called
if(!mFile) {
NS_NewFileSpec(getter_AddRefs(mFile));
if(!mFile)
return NS_ERROR_OUT_OF_MEMORY ;
}
// setup mFile
mFile->SetURLString(file_url) ;
return NS_OK ;
}

View File

@@ -0,0 +1,70 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _NET_CACHEDDISKDATA_H_
#define _NET_CACHEDDISKDATA_H_
#include "nsINetDataCacheRecord.h"
#include "nsCOMPtr.h"
#include "nsIDBAccessor.h"
#include "prtypes.h"
#include "nsILoadGroup.h"
#include "nsIFileChannel.h"
#include "nsNetDiskCache.h"
class nsDiskCacheRecord : public nsINetDataCacheRecord
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHERECORD
protected:
nsDiskCacheRecord(nsIDBAccessor* db, nsNetDiskCache* aCache) ;
virtual ~nsDiskCacheRecord() ;
NS_IMETHOD RetrieveInfo(void* aInfo, PRUint32 aInfoLength) ;
NS_IMETHOD Init(const char* key, PRUint32 length) ;
nsresult GenInfo(void) ;
private:
char* mKey ;
PRUint32 mKeyLength ;
PRInt32 mRecordID ;
char* mMetaData ;
PRUint32 mMetaDataLength ;
nsCOMPtr<nsIFileSpec> mFile ;
nsCOMPtr<nsIDBAccessor> mDB ;
void* mInfo ;
PRUint32 mInfoSize ;
PRUint32 mNumChannels ;
nsCOMPtr<nsNetDiskCache> mDiskCache ;
friend class nsDiskCacheRecordChannel ;
friend class nsDBEnumerator ;
friend class nsNetDiskCache ;
} ;
#endif // _NET_CACHEDDISKDATA_H_

View File

@@ -0,0 +1,392 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsDiskCacheRecordChannel.h"
//#include "nsFileTransport.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIOutputStream.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
// This is copied from nsMemCacheChannel, We should consolidate these two.
class WriteStreamWrapper : public nsIOutputStream
{
public:
WriteStreamWrapper(nsDiskCacheRecordChannel* aChannel,
nsIOutputStream *aBaseStream) ;
virtual ~WriteStreamWrapper() ;
static nsresult
Create(nsDiskCacheRecordChannel* aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* *aWrapper) ;
NS_DECL_ISUPPORTS
NS_DECL_NSIBASESTREAM
NS_DECL_NSIOUTPUTSTREAM
private:
nsDiskCacheRecordChannel* mChannel;
nsCOMPtr<nsIOutputStream> mBaseStream;
} ;
// implement nsISupports
NS_IMPL_ISUPPORTS(WriteStreamWrapper, NS_GET_IID(nsIOutputStream))
WriteStreamWrapper::WriteStreamWrapper(nsDiskCacheRecordChannel* aChannel,
nsIOutputStream *aBaseStream)
: mChannel(aChannel), mBaseStream(aBaseStream)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
WriteStreamWrapper::~WriteStreamWrapper()
{
NS_RELEASE(mChannel);
}
nsresult
WriteStreamWrapper::Create(nsDiskCacheRecordChannel*aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* * aWrapper)
{
WriteStreamWrapper *wrapper = new WriteStreamWrapper(aChannel, aBaseStream);
if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(wrapper);
*aWrapper = wrapper;
return NS_OK;
}
NS_IMETHODIMP
WriteStreamWrapper::Write(const char *aBuffer, PRUint32 aCount, PRUint32 *aNumWritten)
{
*aNumWritten = 0;
nsresult rv = mBaseStream->Write(aBuffer, aCount, aNumWritten);
mChannel->NotifyStorageInUse(*aNumWritten);
return rv;
}
NS_IMETHODIMP
WriteStreamWrapper::Flush()
{
return mBaseStream->Flush();
}
NS_IMETHODIMP
WriteStreamWrapper::Close()
{
return mBaseStream->Close();
}
nsDiskCacheRecordChannel::nsDiskCacheRecordChannel(nsDiskCacheRecord *aRecord,
nsILoadGroup *aLoadGroup)
: mRecord(aRecord) ,
mLoadGroup(aLoadGroup)
{
NS_INIT_REFCNT() ;
mRecord->mNumChannels++ ;
}
nsDiskCacheRecordChannel::~nsDiskCacheRecordChannel()
{
mRecord->mNumChannels-- ;
}
// FUR!!
//
// I know that I gave conflicting advice on the issue of file
// transport versus file protocol handler, but I thought that the
// last word was that we would use the raw transport, when I wrote:
//
// > I just thought of an argument for the other side of the coin, i.e. the
// > benefit of *not* reusing the file protocol handler: On the Mac, it's
// > expensive to convert from a string URL to an nsFileSpec, because the Mac
// > is brain-dead and scans every directory on the path to the file. It's
// > cheaper to create a nsFileSpec for a cache file by combining a single,
// > static nsFileSpec that corresponds to the cache directory with the
// > relative path to the cache file (using nsFileSpec's operator +). This
// > operation is optimized on the Mac to avoid the scanning operation.
//
// The Mac guys will eat us alive if we do path string to nsFileSpec
// conversions for every cache file we open.
nsresult
nsDiskCacheRecordChannel::Init(void)
{
char* urlStr ;
mRecord->mFile->GetURLString(&urlStr) ;
nsresult rv ;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->NewChannel("load", // XXX what should this be?
urlStr,
nsnull, // no base uri
mLoadGroup,
nsnull, // no eventsink getter
0,
nsnull, // no original URI
0,
0,
getter_AddRefs(mFileTransport));
return rv ;
}
nsresult
nsDiskCacheRecordChannel::NotifyStorageInUse(PRInt32 aBytesUsed)
{
return mRecord->mDiskCache->m_StorageInUse += aBytesUsed ;
}
// implement nsISupports
NS_IMPL_ISUPPORTS(nsDiskCacheRecordChannel, NS_GET_IID(nsIChannel))
// implement nsIRequest
NS_IMETHODIMP
nsDiskCacheRecordChannel::IsPending(PRBool *aIsPending)
{
*aIsPending = PR_FALSE ;
if(!mFileTransport)
return NS_OK ;
return mFileTransport->IsPending(aIsPending) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Cancel(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Cancel() ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Suspend(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Suspend() ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Resume(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Resume() ;
}
// implement nsIChannel
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetURI(nsIURI * *aURI)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetURI(aURI) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OpenInputStream(PRUint32 aStartPosition,
PRInt32 aReadCount,
nsIInputStream* *aResult)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->OpenInputStream(aStartPosition,
aReadCount,
aResult) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OpenOutputStream(PRUint32 startPosition,
nsIOutputStream* *aResult)
{
nsresult rv ;
NS_ENSURE_ARG(aResult) ;
nsCOMPtr<nsIOutputStream> outputStream ;
PRUint32 oldLength ;
mRecord->GetStoredContentLength(&oldLength) ;
if(startPosition < oldLength) {
NotifyStorageInUse(startPosition - oldLength) ;
// we should truncate the file at here.
rv = mRecord->SetStoredContentLength(startPosition) ;
if(NS_FAILED(rv)) {
printf(" failed to truncate\n") ;
return rv ;
}
}
rv = mFileTransport->OpenOutputStream(startPosition, getter_AddRefs(outputStream)) ;
if(NS_FAILED(rv)) return rv ;
return WriteStreamWrapper::Create(this, outputStream, aResult) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncOpen(nsIStreamObserver *observer,
nsISupports *ctxt)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->AsyncOpen(observer, ctxt) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncRead(PRUint32 aStartPosition,
PRInt32 aReadCount,
nsISupports *aContext,
nsIStreamListener *aListener)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->AsyncRead(aStartPosition ,
aReadCount ,
aContext ,
aListener) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncWrite(nsIInputStream *fromStream,
PRUint32 startPosition,
PRInt32 writeCount,
nsISupports *ctxt,
nsIStreamObserver *observer)
{
/*
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->AsyncWrite(fromStream,
startPosition,
writeCount,
ctxt,
observer) ;
*/
// I can't do this since the write is not monitored, and I won't be
// able to updata the storage.
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetLoadAttributes(aLoadAttributes) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->SetLoadAttributes(aLoadAttributes) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetContentType(char * *aContentType)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetContentType(aContentType) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetContentLength(PRInt32 *aContentLength)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetContentLength(aContentLength) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get() ;
NS_IF_ADDREF(*aOwner) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetOriginalURI(nsIURI* *aURI)
{
// FUR - might need to implement this - not sure
return NS_ERROR_NOT_IMPLEMENTED ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_OK;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,65 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _ns_DiskCacheRecordChannel_h_
#define _ns_DiskCacheRecordChannel_h_
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsDiskCacheRecord.h"
/*
* This class is plagiarized from nsMemCacheChannel
*/
class nsDiskCacheRecordChannel : public nsIChannel
{
public:
nsDiskCacheRecordChannel(nsDiskCacheRecord *aRecord, nsILoadGroup *aLoadGroup);
virtual ~nsDiskCacheRecordChannel() ;
// Declare nsISupports methods
NS_DECL_ISUPPORTS
// Declare nsIRequest methods
NS_DECL_NSIREQUEST
// Declare nsIChannel methods
NS_DECL_NSICHANNEL
nsresult Init(void) ;
private:
nsresult NotifyStorageInUse(PRInt32 aBytesUsed) ;
nsCOMPtr<nsDiskCacheRecord> mRecord ;
nsCOMPtr<nsILoadGroup> mLoadGroup ;
nsCOMPtr<nsISupports> mOwner ;
nsCOMPtr<nsIChannel> mFileTransport ;
friend class WriteStreamWrapper ;
} ;
#endif // _ns_DiskCacheRecordChannel_h_

View File

@@ -0,0 +1,60 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _NS_IDBACCESSOR_H_
#define _NS_IDBACCESSOR_H_
#include "nsISupports.h"
#include "nsIFileSpec.h"
// nsIDBAccessorIID {6AADD4D0-7785-11d3-87FE-000629D01344}
#define NS_IDBACCESSOR_IID \
{ 0x6aadd4d0, 0x7785, 0x11d3, \
{0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44}}
// nsDBAccessorCID {6AADD4D1-7785-11d3-87FE-000629D01344}
#define NS_DBACCESSOR_CID \
{ 0x6aadd4d1, 0x7785, 0x11d3, \
{ 0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44 }}
class nsIDBAccessor : public nsISupports
{
public:
NS_DEFINE_STATIC_IID_ACCESSOR(NS_IDBACCESSOR_IID)
NS_IMETHOD Init(nsIFileSpec* DBFile) = 0 ;
NS_IMETHOD Shutdown(void) = 0 ;
NS_IMETHOD Put(PRInt32 aID, void* anEntry, PRUint32 aLength) = 0 ;
NS_IMETHOD Get(PRInt32 aID, void** anEntry, PRUint32 *aLength) = 0 ;
NS_IMETHOD Del(PRInt32 aID, void* anEntry, PRUint32 aLength) = 0 ;
NS_IMETHOD GetID(const char* key, PRUint32 length, PRInt32* aID) = 0 ;
NS_IMETHOD EnumEntry(void* *anEntry, PRUint32* aLength, PRBool bReset) = 0 ;
} ;
#endif // _NS_IDBACCESSOR_H_

View File

@@ -0,0 +1,691 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsNetDiskCache.h"
#include "nscore.h"
#include "plstr.h"
#include "prprf.h"
#include "prtypes.h"
#include "prio.h"
#include "prsystem.h" // Directory Seperator
#include "plhash.h"
#include "prclist.h"
#include "prmem.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIPref.h"
#include "mcom_db.h"
#include "nsDBEnumerator.h"
#include "nsDiskCacheRecord.h"
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID) ;
static NS_DEFINE_CID(kDBAccessorCID, NS_DBACCESSOR_CID) ;
static const PRUint32 DISK_CACHE_SIZE_DEFAULT = 5*1024*1024 ; // 5MB
static const char * const DISK_CACHE_PREF = "browser.cache.disk_cache_size";
static const char * const CACHE_DIR_PREF = "browser.cache.directory";
class nsDiskCacheRecord ;
nsNetDiskCache::nsNetDiskCache() :
m_Enabled(PR_TRUE) ,
m_NumEntries(0) ,
m_pNextCache(0) ,
m_pDiskCacheFolder(0) ,
m_StorageInUse(0) ,
m_DB(0) ,
m_BaseDirNum(32)
{
// set it to INF for now
m_MaxEntries = (PRUint32)-1 ;
NS_INIT_REFCNT();
}
nsNetDiskCache::~nsNetDiskCache()
{
printf("~nsNetDiskCache\n") ;
NS_IF_RELEASE(m_DB) ;
// FUR!!
// You shouldn't rely on the value of m_BaseDirNum to diagnose whether or not
// a cache corruption has occurred since it's possible that the app does not
// shut down cleanly and a corrupted cache has still not been cleaned up from
// a previous session. My suggestion is that you pick a different scheme for
// renaming the dirs, e.g. rename them as "trash*" and remove all directories
// with this name pattern on shutdown.
// FUR
// I think that, eventually, we also want a distinguished key in the DB which
// means "clean cache shutdown". You clear this flag when the db is first
// opened and set it just before the db is closed. If the db wasn't shutdown
// cleanly in a prior session, i.e. because the app crashed, on startup you
// scan all the individual files in directories and look for "orphans",
// i.e. cache files which don't have corresponding entries in the db. That's
// also when storage-in-use and number of entries would be recomputed.
//
// We don't necessarily need all this functionality immediately, though.
if(m_BaseDirNum > 32)
RemoveDirs(32) ;
}
NS_IMETHODIMP
nsNetDiskCache::Init(void)
{
nsresult rv ;
// FUR!!
// I really don't think prefs belong here, since that breaks modularity. It
// presupposes that the file cache code is embedded in the browser or some
// other application that uses the prefs, i.e. the code might be used in a
// standalone cache manipulation tool or, someday, in server code. Pref
// reading belongs at a higher level, either in the application itself or
// possibly the I/O manager.
// Also, Init() needs to be lazy, since folder name is not set on startup,
// i.e. need a call to MaybeInit() at the beginning of every public method
NS_WITH_SERVICE(nsIPref, pref, kPrefCID, &rv) ;
if (NS_FAILED(rv))
NS_ERROR("Failed to get globle preference!\n") ;
rv = NS_NewFileSpec(getter_AddRefs(m_pDiskCacheFolder));
if (!m_pDiskCacheFolder) {
NS_ERROR("ERROR: Could not make a file spec.\n") ;
return NS_ERROR_OUT_OF_MEMORY ;
}
char* tempPref = 0 ;
if(pref) {
PRInt32 nTemp = 0 ;
/*
rv = pref->CopyCharPref(CACHE_DIR_PREF, &tempPref) ;
if (NS_SUCCEEDED(rv)) {
printf("cache dir is %s\n", tempPref) ;
m_pDiskCacheFolder->SetUnixStyleFilePath(tempPref) ;
PR_Free(tempPref) ;
} else */
{
m_pDiskCacheFolder->SetUnixStyleFilePath("/tmp") ;
printf("using default folder, /tmp\n") ;
}
}
else {
// temp hack for now. change later for other platform
m_pDiskCacheFolder->SetUnixStyleFilePath("/tmp") ;
}
// FUR - suggest you use nsCOMPtr for m_DB - it will eliminate
// manual addref/release and reduce likelihood of bugs
NS_IF_RELEASE(m_DB) ;
m_DB = new nsDBAccessor() ;
if(!m_DB)
return NS_ERROR_OUT_OF_MEMORY ;
else
NS_ADDREF(m_DB) ;
rv = InitDB() ;
// try once for recovery
if(rv == NS_ERROR_FAILURE) {
rv = DBRecovery() ;
return rv ;
}
rv = UpdateInfo() ;
return rv ;
}
NS_IMETHODIMP
nsNetDiskCache::InitDB(void)
{
// create cache sub directories
nsresult rv ;
nsCOMPtr<nsIFileSpec> cacheSubDir;
rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir));
// FUR - any way to avoid doing this, if it's already been done ?
for (int i=0; i < 32; i++) {
rv = cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
char dirName[3];
PR_snprintf (dirName, 3, "%0.2x", i);
cacheSubDir->AppendRelativeUnixPath (dirName) ;
CreateDir(cacheSubDir);
}
NS_NewFileSpec(getter_AddRefs(m_DBFile)) ;
// FUR - check for NS_NewFileSpec failure
rv = m_DBFile->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
m_DBFile->AppendRelativeUnixPath("cache.db") ;
rv = m_DB->Init(m_DBFile) ;
return rv ;
}
//////////////////////////////////////////////////////////////////////////
// nsISupports methods
// FUR - Suggest you use NS_IMPL_ISUPPORTS3() macro instead
NS_IMETHODIMP
nsNetDiskCache::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_ASSERTION(aInstancePtr, "no instance pointer");
if(aIID.Equals(NS_GET_IID(nsINetDataDiskCache)) ||
aIID.Equals(NS_GET_IID(nsINetDataCache)) ||
aIID.Equals(NS_GET_IID(nsISupports))) {
*aInstancePtr = NS_STATIC_CAST(nsINetDataDiskCache*, this);
NS_ADDREF_THIS();
return NS_OK;
}
else
return NS_NOINTERFACE ;
}
NS_IMPL_ADDREF(nsNetDiskCache) ;
NS_IMPL_RELEASE(nsNetDiskCache) ;
///////////////////////////////////////////////////////////////////////////
// nsINetDataCache Method
NS_IMETHODIMP
nsNetDiskCache::GetDescription(PRUnichar* *aDescription)
{
nsAutoString description("Disk Cache") ;
*aDescription = description.ToNewUnicode() ;
if(!*aDescription)
return NS_ERROR_OUT_OF_MEMORY ;
return NS_OK ;
}
/* don't alloc mem for nsICachedNetData.
* RecordID is generated using the same scheme in nsCacheDiskData,
* see GetCachedNetData() for detail.
*/
NS_IMETHODIMP
nsNetDiskCache::Contains(const char* key, PRUint32 length, PRBool *_retval)
{
*_retval = PR_FALSE ;
NS_ASSERTION(m_DB, "no db.") ;
PRInt32 id = 0 ;
m_DB->GetID(key, length, &id) ;
// FUR - Check for GetID failure ?
void* info = 0 ;
PRUint32 info_size = 0 ;
nsresult rv = m_DB->Get(id, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info)
*_retval = PR_TRUE ;
return NS_OK ;
}
/* regardless if it's cached or not, a copy of nsNetDiskCache would
* always be returned. so release it appropriately.
* if mem alloced, updata m_NumEntries also.
* for now, the new nsCachedNetData is not written into db yet since
* we have nothing to write.
*/
NS_IMETHODIMP
nsNetDiskCache::GetCachedNetData(const char* key, PRUint32 length, nsINetDataCacheRecord **_retval)
{
NS_ASSERTION(m_DB, "no db.") ;
nsresult rv = 0 ;
if (!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
PRInt32 id = 0 ;
m_DB->GetID(key, length, &id) ;
// FUR - Check for GetID failure ?
// construct an empty record
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(m_DB, this) ;
if(!newRecord)
return NS_ERROR_OUT_OF_MEMORY ;
rv = newRecord->Init(key, length) ;
if(NS_FAILED(rv)) {
delete newRecord ;
return rv ;
}
NS_ADDREF(newRecord) ; // addref for _retval
*_retval = (nsINetDataCacheRecord*) newRecord ;
void* info = 0 ;
PRUint32 info_size = 0 ;
rv = m_DB->Get(id, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info) {
nsresult r1 ;
r1 = newRecord->RetrieveInfo(info, info_size) ;
// FUR!! need to release and return error if RetrieveInfo() fails
if(NS_SUCCEEDED(rv))
return NS_OK ;
else
return r1;
} else if (NS_SUCCEEDED(rv) && !info) {
// this is a new record.
m_NumEntries ++ ;
return NS_OK ;
} else
return rv ;
}
/* get an nsICachedNetData, mem needs to be de-alloced if not found. */
NS_IMETHODIMP
nsNetDiskCache::GetCachedNetDataByID(PRInt32 RecordID, nsINetDataCacheRecord **_retval)
{
NS_ASSERTION(m_DB, "no db.") ;
if (!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsresult rv ;
void* info = 0 ;
PRUint32 info_size = 0 ;
rv = m_DB->Get(RecordID, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info) {
// construct an empty record if only found in db
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(m_DB, this) ;
if(!newRecord)
return NS_ERROR_OUT_OF_MEMORY ;
NS_ADDREF(newRecord) ; // addref for _retval
rv = newRecord->RetrieveInfo(info, info_size) ;
if(NS_SUCCEEDED(rv)) {
*_retval = (nsINetDataCacheRecord*) newRecord ;
return NS_OK ;
}
else {
// bad record, I guess
NS_RELEASE(newRecord) ; // release if bad things happen
return rv ;
}
} else {
NS_ERROR("Error: RecordID not in DB\n") ;
return rv ;
}
}
NS_IMETHODIMP
nsNetDiskCache::GetEnabled(PRBool *aEnabled)
{
*aEnabled = m_Enabled ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetEnabled(PRBool aEnabled)
{
m_Enabled = aEnabled ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetFlags(PRUint32 *aFlags)
{
*aFlags = FILE_PER_URL_CACHE;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetNumEntries(PRUint32 *aNumEntries)
{
*aNumEntries = m_NumEntries ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetMaxEntries(PRUint32 *aMaxEntries)
{
*aMaxEntries = m_MaxEntries ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::NewCacheEntryIterator(nsISimpleEnumerator **_retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsISimpleEnumerator* enumerator = new nsDBEnumerator(m_DB, this) ;
if(enumerator) {
NS_ADDREF(enumerator) ;
*_retval = enumerator ;
return NS_OK ;
}
else
return NS_ERROR_OUT_OF_MEMORY ;
}
NS_IMETHODIMP
nsNetDiskCache::GetNextCache(nsINetDataCache * *aNextCache)
{
if(!aNextCache)
return NS_ERROR_NULL_POINTER ;
*aNextCache = m_pNextCache ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetNextCache(nsINetDataCache *aNextCache)
{
m_pNextCache = aNextCache ;
return NS_OK ;
}
// db size can always be measured at the last minute. Since it's hard
// to know before hand.
NS_IMETHODIMP
nsNetDiskCache::GetStorageInUse(PRUint32 *aStorageInUse)
{
PRUint32 total_size = m_StorageInUse, len = 0 ;
// FUR!!
// GetStorageInUse() can be called hundreds of times per second, i.e. every
// time a buffer of data is written to the cache, so we can't afford to stat
// the db file on every call. I would suggest caching the size of the db and
// invalidating that cached value every time a record is written to the db,
// or even every ten written records.
// add the size of the db.
// m_DBFile->GetFileSize(&len) ;
// total_size += len ;
// we need size in kB
total_size = total_size >> 10 ;
*aStorageInUse = total_size ;
return NS_OK ;
}
/*
* The whole cache dirs can be whiped clean since all the cache
* files are resides in seperate hashed dirs. It's safe to do so.
*/
NS_IMETHODIMP
nsNetDiskCache::RemoveAll(void)
{
nsresult rv = RemoveDirs(0) ;
if(NS_FAILED(rv))
return rv ;
// don't forget the db file itself
m_DB->Shutdown() ;
nsFileSpec dbfile ;
m_DBFile->GetFileSpec(&dbfile) ;
dbfile.Delete(PR_TRUE) ;
// reinitilize
rv = InitDB() ;
if(NS_FAILED(rv))
return rv ;
rv = UpdateInfo() ;
return rv ;
}
//////////////////////////////////////////////////////////////////
// nsINetDataDiskCache methods
NS_IMETHODIMP
nsNetDiskCache::GetDiskCacheFolder(nsIFileSpec * *aDiskCacheFolder)
{
*aDiskCacheFolder = m_pDiskCacheFolder ;
NS_ADDREF(*aDiskCacheFolder) ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetDiskCacheFolder(nsIFileSpec * aDiskCacheFolder)
{
char *newfolder, *oldfolder ;
m_pDiskCacheFolder->GetNativePath(&oldfolder) ;
aDiskCacheFolder->GetNativePath(&newfolder) ;
if(PL_strcmp(newfolder, oldfolder) == 0) {
m_pDiskCacheFolder = aDiskCacheFolder ;
// should we do this?
// FUR - no
nsresult rv = RemoveAll() ;
return rv ;
}
else
// FUR
// Need to blow away old cache, build new one
return NS_OK ;
}
//////////////////////////////////////////////////////////////////
// nsNetDiskCache methods
// create a directory (recursively)
NS_IMETHODIMP
nsNetDiskCache::CreateDir(nsIFileSpec* dir_spec)
{
PRBool does_exist ;
nsCOMPtr<nsIFileSpec> p_spec ;
dir_spec->Exists(&does_exist) ;
if(does_exist)
return NS_OK ;
dir_spec->GetParent(getter_AddRefs(p_spec)) ;
// FUR - check return value
p_spec->Exists(&does_exist) ;
if(!does_exist) {
CreateDir(p_spec) ;
dir_spec->CreateDir() ;
// FUR - check return value
}
else {
dir_spec->CreateDir() ;
// FUR - check return value
}
return NS_OK ;
}
// FUR!!
// We can't afford to make a *separate* pass over the whole db on every
// startup, just to figure out m_NumEntries and m_StorageInUse. (This is a
// several second operation on a large db). We'll likely need to store
// distinguished keys in the db that contain these values and update them
// incrementally, except when failure to shut down the db cleanly is detected.
// this will walk through db and update m_NumEntries and m_StorageInUse
NS_IMETHODIMP
nsNetDiskCache::UpdateInfo(void)
{
// count num of entries in db
// NS_ADDREF(this) ; // addref before assign to a nsCOMPtr.
nsISimpleEnumerator* dbEnumerator = new nsDBEnumerator(m_DB, this) ;
if(dbEnumerator)
NS_ADDREF(dbEnumerator) ;
else
return NS_ERROR_FAILURE ;
PRUint32 numEntries = 0, storageInUse = 0, len = 0 ;
PRBool more = PR_FALSE ;
do {
dbEnumerator->HasMoreElements(&more) ;
if(more) {
// update entry number
numEntries++ ;
// update storage in use
nsINetDataCacheRecord* record ;
dbEnumerator->GetNext((nsISupports**)&record) ;
record->GetStoredContentLength(&len) ;
storageInUse += len ;
NS_IF_RELEASE(record) ;
}
} while (more) ;
NS_IF_RELEASE(dbEnumerator) ;
m_NumEntries = numEntries ;
m_StorageInUse = storageInUse ;
printf(" m_NumEntries = %d, size is %d.\n", m_NumEntries, m_StorageInUse) ;
return NS_OK ;
}
// this routine will add m_BaseDirNum to current CacheSubDir names.
// e.g. 00->20, 1f->5f. and update the m_BaseDirNum to another 32.
// the idea is as long as we remember the base number,
// we know how many dirs needs to be removed during shutdown period
// it will be from 0x20 to m_BaseDirNum.
// also, we assume that this operation will not be performed 3 times more
// within a single session. it is part of scavenging routine.
NS_IMETHODIMP
nsNetDiskCache::RenameCacheSubDirs(void)
{
nsCOMPtr<nsIFileSpec> cacheSubDir;
nsresult rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir)) ;
for (int i=0; i < 32; i++) {
rv = cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
char dirName[3];
PR_snprintf(dirName, 3, "%0.2x", i) ;
cacheSubDir->AppendRelativeUnixPath(dirName) ;
// re-name the directory
PR_snprintf(dirName, 3, "%0.2x", i+m_BaseDirNum) ;
rv = cacheSubDir->Rename(dirName) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
}
// update m_BaseDirNum
m_BaseDirNum += 32 ;
return NS_OK ;
}
// this routine will be called everytime we have a db corruption.
NS_IMETHODIMP
nsNetDiskCache::DBRecovery(void)
{
nsresult rv = RenameCacheSubDirs() ;
if(NS_FAILED(rv))
return rv ;
// remove corrupted db file
rv = m_DB->Shutdown() ;
// FUR!!
// You shouldn't return if this fails. Otherwise, it might prevent db deletion
if(NS_FAILED(rv))
return rv ;
nsFileSpec dbfile ;
m_DBFile->GetFileSpec(&dbfile) ;
dbfile.Delete(PR_TRUE) ;
// make sure it's not there any more
PRBool exists = dbfile.Exists() ;
if(exists) {
NS_ERROR("can't remove old db.") ;
return NS_ERROR_FAILURE ;
}
// reinitilize
rv = InitDB() ;
if(NS_FAILED(rv))
return rv ;
rv = UpdateInfo() ;
return rv ;
}
// this routine is used by dtor and RemoveAll() to clean up dirs.
// All directory named from aNum - m_BasedDirNum will be deleted.
NS_IMETHODIMP
nsNetDiskCache::RemoveDirs(PRUint32 aNum)
{
nsCOMPtr<nsIFileSpec> cacheSubDir;
nsresult rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir));
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
for (int i=aNum; i < m_BaseDirNum; i++) {
cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
char dirName[3];
PR_snprintf (dirName, 3, "%0.2x", i);
cacheSubDir->AppendRelativeUnixPath (dirName) ;
nsFileSpec subdir ;
cacheSubDir->GetFileSpec(&subdir) ;
for(nsDirectoryIterator di(subdir, PR_FALSE); di.Exists(); di++) {
di.Spec().Delete(PR_TRUE) ;
}
subdir.Delete(PR_FALSE) ; // recursive delete
}
return NS_OK ;
}

View File

@@ -0,0 +1,82 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
// FUR - Add overall description comment here
#ifndef __gen_nsNetDiskCache_h__
#define __gen_nsNetDiskCache_h__
#include "nsINetDataDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsCOMPtr.h"
#include "nsIPref.h"
#include "nsDBAccessor.h"
class nsIURI; /* forward decl */
class nsICachedNetData; /* forward decl */
class nsISimpleEnumerator; /* forward decl */
class nsIFileSpec; /* forward decl */
/* starting interface: nsNetDiskCache */
class nsNetDiskCache : public nsINetDataDiskCache {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHE
NS_DECL_NSINETDATADISKCACHE
NS_IMETHOD Init(void) ;
nsNetDiskCache() ;
virtual ~nsNetDiskCache() ;
protected:
NS_IMETHOD InitDB(void) ;
NS_IMETHOD CreateDir(nsIFileSpec* dir_spec) ;
NS_IMETHOD UpdateInfo(void) ;
NS_IMETHOD RenameCacheSubDirs(void) ;
NS_IMETHOD DBRecovery(void) ;
NS_IMETHOD RemoveDirs(PRUint32 aNum) ;
private:
PRBool m_Enabled ;
PRUint32 m_NumEntries ;
nsCOMPtr<nsINetDataCache> m_pNextCache ;
nsCOMPtr<nsIFileSpec> m_pDiskCacheFolder ;
nsCOMPtr<nsIFileSpec> m_DBFile ;
PRUint32 m_MaxEntries ;
PRUint32 m_StorageInUse ;
nsIDBAccessor* m_DB ;
// this is used to indicate a db corruption
PRInt32 m_BaseDirNum ;
friend class nsDiskCacheRecord ;
friend class nsDiskCacheRecordChannel ;
} ;
#endif /* __gen_nsNetDiskCache_h__ */

View File

@@ -0,0 +1,32 @@
/*
* 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 Mozilla Communicator.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _nsNetDiskCacheCID_h_
#define _nsNetDiskCacheCID_h_
#define NS_NETDISKCACHE_CID_STR "ECFEEA00-7201-11d3-87FE-000629D01344"
#define NS_NETDISKCACHE_CID \
{ 0xecfeea00, 0x7201, 0x11d3, \
{ 0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44 }}
#endif /* _nsNetDiskCacheCID_h_ */

View File

@@ -0,0 +1,50 @@
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
REQUIRES = libreg xpcom
CPPSRCS = \
diskcache.cpp \
$(NULL)
SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=)
ifdef NO_LD_ARCHIVE_FLAGS
LOST_SYM_LIBS = -lxpcomds_s -lxptinfo -lmozreg_s
endif
LIBS = \
-lmozjs \
-lxpcom \
-lmozdbm_s \
$(MOZ_NECKO_UTIL_LIBS) \
$(LOST_SYM_LIBS) \
$(NSPR_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES = -I$(srcdir)/..
DEFINES += -DUSE_NSREG -DCACHE

View File

@@ -0,0 +1,836 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIStreamListener.h"
#include "nsIStreamObserver.h"
#include "nsIServiceManager.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include <stdio.h>
#include <unistd.h>
#include "nsINetDataCache.h"
#include "nsINetDataCacheRecord.h"
//#include "nsMemCacheCID.h"
#include "nsNetDiskCache.h"
#include "nsIPref.h"
#include "prenv.h"
#include "nsIFileStream.h"
// Number of test entries to be placed in the cache
#define NUM_CACHE_ENTRIES 250
// Cache content stream length will have random length between zero and
// MAX_CONTENT_LENGTH bytes
#define MAX_CONTENT_LENGTH 20000
// Length of random-data cache entry key
#define CACHE_KEY_LENGTH 15
// Length of random-data cache entry meta-data
#define CACHE_METADATA_LENGTH 100
//static NS_DEFINE_CID(kMemCacheCID, NS_MEM_CACHE_FACTORY_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static NS_DEFINE_CID(kDiskCacheCID, NS_NETDISKCACHE_CID) ;
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
static NS_DEFINE_IID(kIPrefIID, NS_IPREF_IID);
// Mapping from test case number to RecordID
static PRInt32 recordID[NUM_CACHE_ENTRIES];
static PRInt32
mapRecordIdToTestNum(PRInt32 aRecordID)
{
int i;
for (i = 0; i < NUM_CACHE_ENTRIES; i++) {
if (recordID[i] == aRecordID)
return i;
}
return -1;
}
// A supply of stream data to either store or compare with
class nsITestDataStream {
public:
virtual ~nsITestDataStream() {};
virtual PRUint32 Next() = 0;
virtual void Read(char* aBuf, PRUint32 aCount) = 0;
virtual PRBool Match(char* aBuf, PRUint32 aCount) = 0;
virtual void Skip(PRUint32 aCount) = 0;
};
// A reproducible stream of random data.
class RandomStream : public nsITestDataStream {
public:
RandomStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState = 1103515245 * mState + 12345;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)(Next() & 0xff))
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
while (aCount--)
Next();
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
// A stream of data that increments on each byte that is read, modulo 256
class CounterStream : public nsITestDataStream {
public:
CounterStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState += 1;
mState &= 0xff;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)Next())
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
mState += aCount;
mState &= 0xff;
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
static int gNumReaders = 0;
static PRUint32 gTotalBytesRead = 0;
static PRUint32 gTotalDuration = 0;
class nsReader : public nsIStreamListener {
public:
NS_DECL_ISUPPORTS
nsReader()
: mStartTime(0), mBytesRead(0)
{
NS_INIT_REFCNT();
gNumReaders++;
}
virtual ~nsReader() {
delete mTestDataStream;
gNumReaders--;
}
nsresult
Init(nsIChannel *aChannel, nsITestDataStream* aRandomStream, PRUint32 aExpectedStreamLength) {
mChannel = aChannel;
mTestDataStream = aRandomStream;
mExpectedStreamLength = aExpectedStreamLength;
mRefCnt = 1;
return NS_OK;
}
NS_IMETHOD OnStartRequest(nsIChannel* channel,
nsISupports* context) {
mStartTime = PR_IntervalNow();
return NS_OK;
}
NS_IMETHOD OnDataAvailable(nsIChannel* channel,
nsISupports* context,
nsIInputStream *aIStream,
PRUint32 aSourceOffset,
PRUint32 aLength) {
char buf[1025];
while (aLength > 0) {
PRUint32 amt;
PRBool match;
aIStream->Read(buf, sizeof buf, &amt);
if (amt == 0) break;
aLength -= amt;
mBytesRead += amt;
match = mTestDataStream->Match(buf, amt);
NS_ASSERTION(match, "Stored data was corrupted on read");
}
return NS_OK;
}
NS_IMETHOD OnStopRequest(nsIChannel* channel,
nsISupports* context,
nsresult aStatus,
const PRUnichar* aMsg) {
PRIntervalTime endTime;
PRIntervalTime duration;
endTime = PR_IntervalNow();
duration = (endTime - mStartTime);
if (NS_FAILED(aStatus)) printf("channel failed.\n");
// printf("read %d bytes\n", mBytesRead);
NS_ASSERTION(mBytesRead == mExpectedStreamLength,
"Stream in cache is wrong length");
gTotalBytesRead += mBytesRead;
gTotalDuration += duration;
// Release channel
mChannel = 0;
return NS_OK;
}
protected:
PRIntervalTime mStartTime;
PRUint32 mBytesRead;
nsITestDataStream* mTestDataStream;
PRUint32 mExpectedStreamLength;
nsCOMPtr<nsIChannel> mChannel;
};
NS_IMPL_ISUPPORTS2(nsReader, nsIStreamListener, nsIStreamObserver)
static nsIEventQueue* eventQueue;
nsresult
InitQueue() {
nsresult rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue service");
rv = eventQService->CreateThreadEventQueue();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create event queue");
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQueue);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue for main thread");
return NS_OK;
}
// Process events until all streams are OnStopRequest'ed
nsresult
WaitForEvents() {
while (gNumReaders) {
eventQueue->ProcessPendingEvents();
}
return NS_OK;
}
// Read data for a single cache record and compare against testDataStream
nsresult
TestReadStream(nsINetDataCacheRecord *record, nsITestDataStream *testDataStream,
PRUint32 expectedStreamLength)
{
nsCOMPtr<nsIChannel> channel;
nsresult rv;
PRUint32 actualContentLength;
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = record->GetStoredContentLength(&actualContentLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(actualContentLength == expectedStreamLength,
"nsINetDataCacheRecord::GetContentLength() busted ?");
nsReader *reader = new nsReader;
reader->AddRef();
rv = reader->Init(channel, testDataStream, expectedStreamLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->AsyncRead(0, -1, 0, reader);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
reader->Release();
return NS_OK;
}
// Check that records can be retrieved using their record-ID, in addition
// to using the opaque key.
nsresult
TestRecordID(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetDataByID(recordID[testNum], getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't obtain record using record ID");
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
return NS_OK;
}
// Check that all cache entries in the database are enumerated and that
// no duplicates appear.
nsresult
TestEnumeration(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsISupports> tempISupports;
nsCOMPtr<nsISimpleEnumerator> iterator;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
PRInt32 recID;
int numRecords = 0;
// Iterate over all records in the cache
rv = cache->NewCacheEntryIterator(getter_AddRefs(iterator));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create new cache entry iterator");
PRBool notDone;
while (1) {
// Done iterating ?
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
// Get next record in iteration
rv = iterator->GetNext(getter_AddRefs(tempISupports));
NS_ASSERTION(NS_SUCCEEDED(rv), "iterator bustage");
record = do_QueryInterface(tempISupports);
numRecords++;
// Get record ID
rv = record->GetRecordID(&recID);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
testNum = mapRecordIdToTestNum(recID);
NS_ASSERTION(testNum != -1, "Corrupted Record ID ?");
// Erase mapping from table, so that duplicate enumerations are detected
recordID[testNum] = -1;
// Make sure stream matches test data
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
NS_ASSERTION(numRecords == NUM_CACHE_ENTRIES, "Iteration bug");
return NS_OK;
}
// Read the test data that was written in FillCache(), checking for
// corruption, truncation.
nsresult
TestRead(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData, *storedCacheKey;
PRUint32 testNum, storedCacheKeyLength;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Ensure that entry is in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
// Match against previously stored meta-data
match = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
// Test GetKey() method
rv = record->GetKey(&storedCacheKeyLength, &storedCacheKey);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
(storedCacheKeyLength == sizeof cacheKey) &&
!memcmp(storedCacheKey, &cacheKey[0], sizeof cacheKey),
"nsINetDataCacheRecord::GetKey failed");
nsAllocator::Free(storedCacheKey);
PRUint32 expectedStreamLength = randomStream->Next() & 0xffff;
TestReadStream(record, randomStream, expectedStreamLength);
}
WaitForEvents();
// Compute rate in MB/s
double rate = gTotalBytesRead / PR_IntervalToMilliseconds(gTotalDuration);
rate *= NUM_CACHE_ENTRIES;
rate *= 1000;
rate /= (1024 * 1024);
printf("Read %d bytes at a rate of %5.1f MB per second \n",
gTotalBytesRead, rate);
return NS_OK;
}
// Repeatedly call SetStoredContentLength() on a cache entry and make
// read the stream's data to ensure that it's not corrupted by the effect
nsresult
TestTruncation(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
char cacheKey[CACHE_KEY_LENGTH];
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
randomStream->Skip(CACHE_METADATA_LENGTH);
PRUint32 initialStreamLength = randomStream->Next() & 0xffff;
delete randomStream;
PRUint32 i;
PRUint32 delta = initialStreamLength / 64;
for (i = initialStreamLength; i >= delta; i -= delta) {
PRUint32 expectedStreamLength = i;
// Do the truncation
record->SetStoredContentLength(expectedStreamLength);
randomStream = new RandomStream(0);
randomStream->Skip(CACHE_KEY_LENGTH + CACHE_METADATA_LENGTH + 1);
TestReadStream(record, randomStream, expectedStreamLength);
WaitForEvents();
}
return NS_OK;
}
// Write known data to random offsets in a single cache entry and test
// resulting stream for correctness.
nsresult
TestOffsetWrites(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[512];
char cacheKey[CACHE_KEY_LENGTH];
RandomStream *randomStream;
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
nsCOMPtr<nsIFileSpec> file ;
record->GetFilename(getter_AddRefs(file)) ;
char* name ;
file->GetUnixStyleFilePath(&name) ;
printf(" file name is %s \n", name) ;
// Write buffer-fulls of data at random offsets into the cache entry.
// Data written is (offset % 0xff)
PRUint32 startingOffset;
PRUint32 streamLength = 0;
PRUint32 len = 0 ;
CounterStream *counterStream;
int i = 0;
for (i = 0; i < 257; i++) {
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
startingOffset = streamLength ? streamLength - (randomStream->Next() % sizeof buf): 0;
rv = channel->OpenOutputStream(startingOffset, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
truncate(name, startingOffset) ;
counterStream = new CounterStream(startingOffset);
counterStream->Read(buf, sizeof buf);
nsresult status ;
nsCOMPtr<nsIRandomAccessStore> ras = do_QueryInterface(outStream, &status);
if (NS_FAILED(status)) {
// mState = END_WRITE;
return NS_ERROR_FAILURE;
}
PRIntn offset ;
ras->Tell(&offset) ;
// printf(" offset is %d \n", offset) ;
PRUint32 numWritten;
rv = outStream->Write(buf, sizeof buf, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == sizeof buf, "Write() bug?");
streamLength = startingOffset + sizeof buf;
rv = outStream->Close();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't close channel");
delete counterStream;
record->GetStoredContentLength(&len) ;
if(len != streamLength)
printf(" offset = %d is wrong, filesize = %d\n", startingOffset, len) ;
}
/*
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
startingOffset = 208;
rv = channel->OpenOutputStream(startingOffset, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
counterStream = new CounterStream(startingOffset);
counterStream->Read(buf, sizeof buf);
nsresult status ;
nsCOMPtr<nsIRandomAccessStore> ras = do_QueryInterface(outStream, &status);
if (NS_FAILED(status)) {
// mState = END_WRITE;
return NS_ERROR_FAILURE;
}
PRIntn offset = 0 ;
ras->Tell(&offset) ;
printf(" offset is %d \n", offset) ;
PRUint32 numWritten;
rv = outStream->Write(buf, sizeof buf, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == sizeof buf, "Write() bug?");
streamLength = startingOffset + sizeof buf;
rv = outStream->Close();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't close channel");
delete counterStream;
record->GetStoredContentLength(&len) ;
if(len != streamLength)
printf(" offset = %d is wrong, filesize = %d\n", startingOffset, len) ;
*/
delete randomStream;
counterStream = new CounterStream(0);
TestReadStream(record, counterStream, streamLength);
WaitForEvents();
return NS_OK;
}
// Create entries in the network data cache, using random data for the
// key, the meta-data and the stored content data.
nsresult
FillCache(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[1000];
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char metaData[CACHE_METADATA_LENGTH];
PRUint32 testNum;
char *data;
RandomStream *randomStream;
PRIntervalTime startTime = PR_IntervalNow();
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// No entry should be in cache until we add it
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(!inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
// Test nsINetDataCacheRecord::GetRecordID()
rv = record->GetRecordID(&recordID[testNum]);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
// Test nsINetDataCache::GetNumEntries()
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == testNum + 1, "GetNumEntries failure");
// Record meta-data should be initially empty
rv = record->GetMetaData(&metaDataLength, &data);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
if ((metaDataLength != 0) || (data != 0))
return NS_ERROR_FAILURE;
// Store random data as meta-data
randomStream->Read(metaData, sizeof metaData);
record->SetMetaData(sizeof metaData, metaData);
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->OpenOutputStream(0, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
PRUint32 beforeOccupancy;
rv = cache->GetStorageInUse(&beforeOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
int streamLength = randomStream->Next() & 0xffff;
int remaining = streamLength;
while (remaining) {
PRUint32 numWritten;
int amount = PR_MIN(sizeof buf, remaining);
randomStream->Read(buf, amount);
rv = outStream->Write(buf, amount, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == (PRUint32)amount, "Write() bug?");
remaining -= amount;
}
outStream->Close();
PRUint32 afterOccupancy;
rv = cache->GetStorageInUse(&afterOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 streamLengthInKB = streamLength >> 10;
NS_ASSERTION((afterOccupancy - beforeOccupancy) >= streamLengthInKB,
"nsINetDataCache::GetStorageInUse() is busted");
// *Now* there should be an entry in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
delete randomStream;
}
PRIntervalTime endTime = PR_IntervalNow();
return NS_OK;
}
nsresult NS_AutoregisterComponents()
{
nsresult rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup,
NULL /* default */);
return rv;
}
PRBool initPref ()
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefPtr, kPrefCID, &rv);
if (NS_FAILED(rv))
return false;
nsCOMPtr<nsIFileSpec> fileSpec;
rv = NS_NewFileSpec (getter_AddRefs(fileSpec));
if (NS_FAILED(rv))
return false;
nsCString defaultPrefFile = PR_GetEnv ("MOZILLA_FIVE_HOME");
if (defaultPrefFile.Length())
defaultPrefFile += "/";
else
defaultPrefFile = "./";
defaultPrefFile += "default_prefs.js";
fileSpec->SetUnixStyleFilePath (defaultPrefFile.GetBuffer());
PRBool exists = false;
fileSpec->Exists(&exists);
if (exists)
prefPtr->ReadUserPrefsFrom(fileSpec);
else
return false;
return true;
}
int
main(int argc, char* argv[])
{
initPref() ;
nsresult rv;
nsCOMPtr<nsINetDataCache> cache;
rv = NS_AutoregisterComponents();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't register XPCOM components");
rv = nsComponentManager::CreateInstance(kDiskCacheCID, nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(cache));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create memory cache factory");
InitQueue();
PRUnichar* description;
rv = cache->GetDescription(&description);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache description");
nsCAutoString descStr(description);
printf("Testing: %s\n", descStr.GetBuffer());
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
PRUint32 startOccupancy;
rv = cache->GetStorageInUse(&startOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
rv = FillCache(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't fill cache with random test data");
rv = TestRead(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't read random test data from cache");
rv = TestRecordID(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't index records using record ID");
rv = TestEnumeration(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully enumerate records");
rv = TestTruncation(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully truncate records");
rv = TestOffsetWrites(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully write to records using non-zero offsets");
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
PRUint32 endOccupancy;
rv = cache->GetStorageInUse(&endOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
NS_ASSERTION(startOccupancy == endOccupancy, "Cache occupancy not correctly computed ?");
return 0;
}

View File

@@ -0,0 +1,46 @@
# Generated automatically from Makefile.in by configure.
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
VPATH = @srcdir@
srcdir = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkmemcache_s
REQUIRES = nspr dbm
EXPORTS=nsMemCacheCID.h
CPPSRCS = \
nsMemCache.cpp \
nsMemCacheRecord.cpp \
nsMemCacheChannel.cpp \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,42 @@
#!nmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkmemcache_s
CPP_OBJS = \
.\$(OBJDIR)\nsMemCache.obj \
.\$(OBJDIR)\nsMemCacheRecord.obj \
.\$(OBJDIR)\nsMemCacheChannel.obj \
$(NULL)
EXPORTS=nsMemCacheCID.h
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -0,0 +1,334 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
/**
* nsMemCache is the implementation of an in-memory network-data
* cache, used to cache the responses to network retrieval commands.
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by
* two different keys: a record id number and an opaque key, which is
* created by the cache manager by combining the URI with a "secondary
* key", e.g. HTTP post data.
*/
#include "nsMemCache.h"
#include "nsMemCacheRecord.h"
#include "nsIGenericFactory.h"
#include "nsString.h"
#include "nsHashtable.h"
#include "nsHashtableEnumerator.h"
#include "nsEnumeratorUtils.h"
PRInt32 nsMemCache::gRecordSerialNumber = 0;
nsMemCache::nsMemCache()
: mNumEntries(0), mOccupancy(0), mEnabled(PR_TRUE),
mHashTable(0)
{
NS_INIT_REFCNT();
}
nsMemCache::~nsMemCache()
{
nsresult rv;
rv = RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv) && (mNumEntries == 0),
"Failure to shut down memory cache. "
"Somewhere, someone is holding references to at least one cache record");
delete mHashTable;
}
nsresult
nsMemCache::Init()
{
mHashTable = new nsHashtable(256);
if (!mHashTable)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsMemCache, NS_GET_IID(nsINetDataCache))
NS_IMETHODIMP
nsMemCache::GetDescription(PRUnichar * *aDescription)
{
nsAutoString description("Memory Cache");
*aDescription = description.ToNewUnicode();
if (!*aDescription)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::Contains(const char *aKey, PRUint32 aKeyLength, PRBool *aFound)
{
nsOpaqueKey *opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
if (!opaqueKey)
return NS_ERROR_OUT_OF_MEMORY;
*aFound = mHashTable->Exists(opaqueKey);
delete opaqueKey;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetCachedNetData(const char *aKey, PRUint32 aKeyLength,
nsINetDataCacheRecord* *aRecord)
{
nsresult rv;
nsMemCacheRecord* record = 0;
nsOpaqueKey *opaqueKey2 = 0;
nsOpaqueKey *opaqueKey3 = 0;
nsOpaqueKey *opaqueKey;
opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
if (!opaqueKey)
goto out_of_memory;
record = (nsMemCacheRecord*)mHashTable->Get(opaqueKey);
delete opaqueKey;
// No existing cache database entry was found. Create a new one.
// This requires two mappings in the hash table:
// Record ID ==> record
// Opaque key ==> record
if (!record) {
record = new nsMemCacheRecord;
if (!record)
goto out_of_memory;
rv = record->Init(aKey, aKeyLength, ++gRecordSerialNumber, this);
if (NS_FAILED(rv)) goto out_of_memory;
// Index the record by opaque key
opaqueKey2 = new nsOpaqueKey(record->mKey, record->mKeyLength);
if (!opaqueKey2) goto out_of_memory;
mHashTable->Put(opaqueKey2, record);
// Index the record by it's record ID
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &record->mRecordID);
opaqueKey3 = new nsOpaqueKey(recordIDbytes,
sizeof record->mRecordID);
if (!opaqueKey3) {
// Clean up the first record from the hash table
mHashTable->Remove(opaqueKey);
goto out_of_memory;
}
mHashTable->Put(opaqueKey3, record);
// The hash table holds on to the record
record->AddRef();
delete opaqueKey2;
delete opaqueKey3;
mNumEntries++;
}
record->AddRef();
*aRecord = record;
return NS_OK;
out_of_memory:
delete opaqueKey2;
delete opaqueKey3;
delete record;
return NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsMemCache::GetCachedNetDataByID(PRInt32 RecordID,
nsINetDataCacheRecord* *aRecord)
{
nsOpaqueKey opaqueKey(NS_REINTERPRET_CAST(const char *, &RecordID),
sizeof RecordID);
*aRecord = (nsINetDataCacheRecord*)mHashTable->Get(&opaqueKey);
if (*aRecord) {
NS_ADDREF(*aRecord);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_METHOD
nsMemCache::Delete(nsMemCacheRecord* aRecord)
{
nsMemCacheRecord *removedRecord;
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &aRecord->mRecordID);
nsOpaqueKey opaqueRecordIDKey(recordIDbytes,
sizeof aRecord->mRecordID);
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueRecordIDKey);
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
nsOpaqueKey opaqueKey(aRecord->mKey, aRecord->mKeyLength);
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueKey);
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
aRecord->Release();
mNumEntries--;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetEnabled(PRBool *aEnabled)
{
NS_ENSURE_ARG(aEnabled);
*aEnabled = mEnabled;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::SetEnabled(PRBool aEnabled)
{
mEnabled = aEnabled;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetFlags(PRUint32 *aFlags)
{
NS_ENSURE_ARG(aFlags);
*aFlags = MEMORY_CACHE;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetNumEntries(PRUint32 *aNumEntries)
{
NS_ENSURE_ARG(aNumEntries);
*aNumEntries = mNumEntries;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetMaxEntries(PRUint32 *aMaxEntries)
{
NS_ENSURE_ARG(aMaxEntries);
*aMaxEntries = MEM_CACHE_MAX_ENTRIES;
return NS_OK;
}
static NS_METHOD
HashEntryConverter(nsHashKey *aKey, void *aValue,
void *unused, nsISupports **retval)
{
nsMemCacheRecord *record;
nsOpaqueKey *opaqueKey;
record = (nsMemCacheRecord*)aValue;
opaqueKey = (nsOpaqueKey*)aKey;
// Hash table keys that index cache entries by their record ID
// shouldn't be enumerated.
if ((opaqueKey->GetKeyLength() == sizeof(PRInt32))) {
#ifdef DEBUG
PRInt32 recordID;
record->GetRecordID(&recordID);
NS_ASSERTION(*((PRInt32*)opaqueKey->GetKey()) == recordID,
"Key has incorrect key length");
#endif
return NS_ERROR_FAILURE;
}
NS_IF_ADDREF(record);
*retval = NS_STATIC_CAST(nsISupports*, record);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::NewCacheEntryIterator(nsISimpleEnumerator* *aIterator)
{
nsCOMPtr<nsIEnumerator> iterator;
NS_ENSURE_ARG(aIterator);
NS_NewHashtableEnumerator(mHashTable, HashEntryConverter,
mHashTable, getter_AddRefs(iterator));
return NS_NewAdapterEnumerator(aIterator, iterator);
}
NS_IMETHODIMP
nsMemCache::GetNextCache(nsINetDataCache* *aNextCache)
{
NS_ENSURE_ARG(aNextCache);
*aNextCache = mNextCache;
NS_ADDREF(*aNextCache);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::SetNextCache(nsINetDataCache* aNextCache)
{
mNextCache = aNextCache;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetStorageInUse(PRUint32 *aStorageInUse)
{
NS_ENSURE_ARG(aStorageInUse);
// Convert from bytes to KB
*aStorageInUse = (mOccupancy >> 10);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::RemoveAll(void)
{
PRBool failed;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> recordSupports;
nsCOMPtr<nsINetDataCacheRecord> record;
nsresult rv;
failed = PR_FALSE;
rv = NewCacheEntryIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv))
return rv;
PRBool notDone;
while (1) {
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(recordSupports));
record = do_QueryInterface(recordSupports);
recordSupports = 0;
PRUint32 bytesUsed;
record->GetStoredContentLength(&bytesUsed);
rv = record->Delete();
if (NS_FAILED(rv)) {
failed = PR_TRUE;
continue;
}
mOccupancy -= bytesUsed;
}
if (failed)
return NS_ERROR_FAILURE;
return NS_OK;
}

View File

@@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
/**
* nsMemCache is the implementation of an in-memory network-data
* cache, used to cache the responses to network retrieval commands.
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by
* two different keys: a record id number and an opaque key, which is
* created by the cache manager by combining the URI with a "secondary
* key", e.g. HTTP post data.
*/
#ifndef _nsMemCache_h_
#define _nsMemCache_h_
#include "nsINetDataCache.h"
// Maximum number of URIs that may be resident in the cache
#define MEM_CACHE_MAX_ENTRIES 1000
#define MEM_CACHE_SEGMENT_SIZE (1 << 12)
#define MEM_CACHE_MAX_ENTRY_SIZE (1 << 20)
class nsHashtable;
class nsMemCacheRecord;
class nsMemCache : public nsINetDataCache
{
public:
nsMemCache();
virtual ~nsMemCache();
nsresult Init();
// nsISupports methods
NS_DECL_ISUPPORTS
// nsINetDataCache methods
NS_DECL_NSINETDATACACHE
// Factory
static NS_METHOD nsMemCacheConstructor(nsISupports *aOuter, REFNSIID aIID,
void **aResult);
protected:
PRUint32 mNumEntries;
PRUint32 mOccupancy; // Memory used, in bytes
PRBool mEnabled; // If false, bypass mem cache
nsINetDataCache* mNextCache;
// Mapping from either opaque key or record ID to nsMemCacheRecord
nsHashtable* mHashTable;
// Used to assign record ID's
static PRInt32 gRecordSerialNumber;
NS_METHOD Delete(nsMemCacheRecord* aRecord);
friend class nsMemCacheRecord;
friend class nsMemCacheChannel;
};
#endif // _nsMemCache_h_

View File

@@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
// XPCOM Class ID for the network data in-memory cache
#ifndef nsMEMCACHECID_h__
#define nsMEMCACHECID_h__
// {e4710560-7de2-11d3-90cb-0040056a906e}
#define NS_MEM_CACHE_FACTORY_CID \
{ \
0xe4710560, \
0x7de2, \
0x11d3, \
{0x90, 0xcb, 0x00, 0x40, 0x05, 0x6a, 0x90, 0x6e} \
}
#endif // nsMEMCACHECID_h__

View File

@@ -0,0 +1,464 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsMemCache.h"
#include "nsMemCacheChannel.h"
#include "nsIStreamListener.h"
#include "nsIChannel.h"
#include "nsIStorageStream.h"
#include "nsIOutputStream.h"
#include "nsIServiceManager.h"
#include "nsIEventQueueService.h"
#include "nsNetUtil.h"
#include "nsILoadGroup.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID);
NS_IMPL_ISUPPORTS(nsMemCacheChannel, NS_GET_IID(nsIChannel))
void
nsMemCacheChannel::NotifyStorageInUse(PRInt32 aBytesUsed)
{
mRecord->mCache->mOccupancy += aBytesUsed;
}
/**
* This class acts as an adaptor around a synchronous input stream to add async
* read capabilities. It adds methods for initiating, suspending, resuming and
* cancelling async reads.
*/
class AsyncReadStreamAdaptor : public nsIInputStream {
public:
AsyncReadStreamAdaptor(nsMemCacheChannel* aChannel, nsIInputStream *aSyncStream):
mSyncStream(aSyncStream), mDataAvailCursor(0),
mRemaining(0), mChannel(aChannel), mAvailable(0), mAborted(false), mSuspended(false)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
virtual ~AsyncReadStreamAdaptor() {
mChannel->mAsyncReadStream = 0;
NS_RELEASE(mChannel);
}
NS_DECL_ISUPPORTS
nsresult
IsPending(PRBool* aIsPending) {
*aIsPending = (mRemaining != 0) && !mAborted;
return NS_OK;
}
nsresult
Cancel(void) {
mAborted = true;
return mStreamListener->OnStopRequest(mChannel, mContext, NS_BINDING_ABORTED, nsnull);
}
nsresult
Suspend(void) { mSuspended = true; return NS_OK; }
nsresult
Resume(void) {
if (!mSuspended)
return NS_ERROR_FAILURE;
mSuspended = false;
return NextListenerEvent();
}
NS_IMETHOD
Available(PRUint32 *aNumBytes) { return mAvailable; }
NS_IMETHOD
Read(char* aBuf, PRUint32 aCount, PRUint32 *aBytesRead) {
if (mAborted)
return NS_ERROR_ABORT;
*aBytesRead = 0;
aCount = PR_MIN(aCount, mAvailable);
nsresult rv = mSyncStream->Read(aBuf, aCount, aBytesRead);
mAvailable -= *aBytesRead;
if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) {
Fail();
return rv;
}
if (!mSuspended && !mAvailable) {
rv = NextListenerEvent();
if (NS_FAILED(rv)) {
Fail();
return rv;
}
}
return NS_OK;
}
NS_IMETHOD
Close() {
nsresult rv = mSyncStream->Close();
mSyncStream = 0;
mContext = 0;
mStreamListener = 0;
return rv;
}
nsresult
AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports* aContext, nsIStreamListener* aListener) {
nsresult rv;
nsIEventQueue *eventQ;
mContext = aContext;
mStreamListener = aListener;
mRemaining = aReadCount;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueService, &rv);
if (NS_FAILED(rv)) return rv;
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQ);
if (NS_FAILED(rv)) return rv;
rv = NS_NewAsyncStreamListener(aListener, eventQ,
getter_AddRefs(mStreamListener));
NS_RELEASE(eventQ);
if (NS_FAILED(rv)) return rv;
rv = mStreamListener->OnStartRequest(mChannel, aContext);
if (NS_FAILED(rv)) return rv;
return NextListenerEvent();
}
protected:
nsresult
Fail(void) {
mAborted = true;
return mStreamListener->OnStopRequest(mChannel, mContext, NS_BINDING_FAILED, nsnull);
}
nsresult
NextListenerEvent() {
PRUint32 available;
nsresult rv = mSyncStream->Available(&available);
if (NS_FAILED(rv)) return rv;
available -= mAvailable;
available = PR_MIN(available, mRemaining);
if (available) {
PRUint32 size = PR_MIN(available, MEM_CACHE_SEGMENT_SIZE);
rv = mStreamListener->OnDataAvailable(mChannel, mContext, this,
mDataAvailCursor, size);
mDataAvailCursor += size;
mRemaining -= size;
mAvailable += size;
return rv;
} else {
rv = mStreamListener->OnStopRequest(mChannel, mContext, NS_OK, nsnull);
AsyncReadStreamAdaptor* thisAlias = this;
NS_RELEASE(thisAlias);
return rv;
}
}
private:
nsCOMPtr<nsISupports> mContext; // Opaque context passed to AsyncRead()
nsCOMPtr<nsIStreamListener> mStreamListener; // Stream listener that has been proxied
nsCOMPtr<nsIInputStream> mSyncStream; // Underlying synchronous stream that is
// being converted to an async stream
PRUint32 mDataAvailCursor;
PRUint32 mRemaining; // Size of AsyncRead request less bytes for
// consumer OnDataAvailable's that were fired
PRUint32 mAvailable; // Number of bytes for which OnDataAvailable fired
nsMemCacheChannel* mChannel; // Associated memory cache channel, strong link
// but can not use nsCOMPtr
bool mAborted; // Abort() has been called
bool mSuspended; // Suspend() has been called
};
NS_IMPL_ISUPPORTS(AsyncReadStreamAdaptor, NS_GET_IID(nsIInputStream))
// The only purpose of this output stream wrapper is to adjust the cache's
// overall occupancy as new data flows into the cache entry.
class MemCacheWriteStreamWrapper : public nsIOutputStream {
public:
MemCacheWriteStreamWrapper(nsMemCacheChannel* aChannel, nsIOutputStream *aBaseStream):
mBaseStream(aBaseStream), mChannel(aChannel)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
virtual ~MemCacheWriteStreamWrapper() { NS_RELEASE(mChannel); };
static nsresult
Create(nsMemCacheChannel* aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* *aWrapper) {
MemCacheWriteStreamWrapper *wrapper =
new MemCacheWriteStreamWrapper(aChannel, aBaseStream);
if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(wrapper);
*aWrapper = wrapper;
return NS_OK;
}
NS_DECL_ISUPPORTS
NS_IMETHOD
Write(const char *aBuffer, PRUint32 aCount, PRUint32 *aNumWritten) {
*aNumWritten = 0;
nsresult rv = mBaseStream->Write(aBuffer, aCount, aNumWritten);
mChannel->NotifyStorageInUse(*aNumWritten);
return rv;
}
NS_IMETHOD
Flush() { return mBaseStream->Flush(); }
NS_IMETHOD
Close() { return mBaseStream->Close(); }
private:
nsCOMPtr<nsIOutputStream> mBaseStream;
nsMemCacheChannel* mChannel;
};
NS_IMPL_ISUPPORTS(MemCacheWriteStreamWrapper, NS_GET_IID(nsIOutputStream))
nsMemCacheChannel::nsMemCacheChannel(nsMemCacheRecord *aRecord, nsILoadGroup *aLoadGroup)
: mRecord(aRecord)
{
NS_INIT_REFCNT();
mRecord->mNumChannels++;
}
nsMemCacheChannel::~nsMemCacheChannel()
{
mRecord->mNumChannels--;
}
NS_IMETHODIMP
nsMemCacheChannel::IsPending(PRBool* aIsPending)
{
*aIsPending = PR_FALSE;
if (!mAsyncReadStream)
return NS_OK;
return mAsyncReadStream->IsPending(aIsPending);
}
NS_IMETHODIMP
nsMemCacheChannel::Cancel(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Cancel();
}
NS_IMETHODIMP
nsMemCacheChannel::Suspend(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Suspend();
}
NS_IMETHODIMP
nsMemCacheChannel::Resume(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Resume();
}
NS_IMETHODIMP
nsMemCacheChannel::GetOriginalURI(nsIURI * *aURI)
{
// Not required
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetURI(nsIURI * *aURI)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aResult)
{
nsresult rv;
NS_ENSURE_ARG(aResult);
if (mInputStream)
return NS_ERROR_NOT_AVAILABLE;
rv = mRecord->mStorageStream->NewInputStream(aStartPosition, getter_AddRefs(mInputStream));
*aResult = mInputStream;
NS_ADDREF(*aResult);
return rv;
}
NS_IMETHODIMP
nsMemCacheChannel::OpenOutputStream(PRUint32 startPosition, nsIOutputStream* *aResult)
{
nsresult rv;
NS_ENSURE_ARG(aResult);
nsCOMPtr<nsIOutputStream> outputStream;
PRUint32 oldLength;
mRecord->mStorageStream->GetLength(&oldLength);
rv = mRecord->mStorageStream->GetOutputStream(startPosition, getter_AddRefs(outputStream));
if (NS_FAILED(rv)) return rv;
if (startPosition < oldLength)
NotifyStorageInUse(startPosition - oldLength);
return MemCacheWriteStreamWrapper::Create(this, outputStream, aResult);
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncOpen(nsIStreamObserver *observer, nsISupports *ctxt)
{
// Not required
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener)
{
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = OpenInputStream(aStartPosition, aReadCount, getter_AddRefs(inputStream));
if (NS_FAILED(rv)) return rv;
AsyncReadStreamAdaptor *asyncReadStreamAdaptor;
asyncReadStreamAdaptor = new AsyncReadStreamAdaptor(this, inputStream);
if (!asyncReadStreamAdaptor)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(asyncReadStreamAdaptor);
mAsyncReadStream = asyncReadStreamAdaptor;
rv = asyncReadStreamAdaptor->AsyncRead(aStartPosition, aReadCount, aContext, aListener);
if (NS_FAILED(rv))
delete asyncReadStreamAdaptor;
return rv;
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncWrite(nsIInputStream *fromStream, PRUint32 startPosition,
PRInt32 writeCount, nsISupports *ctxt,
nsIStreamObserver *observer)
{
// Not required to be implemented
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetContentType(char* *aContentType)
{
// Not required to be implemented, since it is implemented by cache manager
// NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
// FIXME - lying for the purpose of testing
*aContentType = strdup("text/html");
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::GetContentLength(PRInt32 *aContentLength)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::SetOwner(nsISupports* aOwner)
{
// Not required to be implemented, since it is implemented by cache manager
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#ifndef _nsMemCacheChannel_h_
#define _nsMemCacheChannel_h_
#include "nsMemCacheRecord.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsCOMPtr.h"
class AsyncReadStreamAdaptor;
class nsMemCacheChannel : public nsIChannel
{
public:
// Constructors and Destructor
nsMemCacheChannel(nsMemCacheRecord *aRecord, nsILoadGroup *aLoadGroup);
virtual ~nsMemCacheChannel();
// Declare nsISupports methods
NS_DECL_ISUPPORTS
// Declare nsIRequest methods
NS_DECL_NSIREQUEST
// Declare nsIChannel methods
NS_DECL_NSICHANNEL
protected:
void NotifyStorageInUse(PRInt32 aBytesUsed);
nsCOMPtr<nsMemCacheRecord> mRecord;
nsCOMPtr<nsIInputStream> mInputStream;
nsCOMPtr<nsISupports> mOwner;
AsyncReadStreamAdaptor* mAsyncReadStream; // non-owning pointer
friend class MemCacheWriteStreamWrapper;
friend class AsyncReadStreamAdaptor;
};
#endif // _nsMemCacheChannel_h_

View File

@@ -0,0 +1,164 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsMemCache.h"
#include "nsMemCacheRecord.h"
#include "nsMemCacheChannel.h"
#include "nsIAllocator.h"
#include "nsStorageStream.h"
static NS_DEFINE_IID(kINetDataCacheRecord, NS_INETDATACACHERECORD_IID);
nsMemCacheRecord::nsMemCacheRecord()
: mKey(0), mKeyLength(0), mMetaData(0), mMetaDataLength(0), mNumChannels(0)
{
NS_INIT_REFCNT();
}
nsMemCacheRecord::~nsMemCacheRecord()
{
if (mMetaData)
delete[] mMetaData;
if (mKey)
delete[] mKey;
}
NS_IMPL_ISUPPORTS(nsMemCacheRecord, NS_GET_IID(nsINetDataCacheRecord))
NS_IMETHODIMP
nsMemCacheRecord::GetKey(PRUint32 *aLength, char **aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = (char *)nsAllocator::Alloc(mKeyLength);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(*aResult, mKey, mKeyLength);
*aLength = mKeyLength;
return NS_OK;
}
nsresult
nsMemCacheRecord::Init(const char *aKey, PRUint32 aKeyLength,
PRUint32 aRecordID, nsMemCache *aCache)
{
nsresult rv;
NS_ASSERTION(!mKey, "Memory cache record key set multiple times");
rv = NS_NewStorageStream(MEM_CACHE_SEGMENT_SIZE, MEM_CACHE_MAX_ENTRY_SIZE,
getter_AddRefs(mStorageStream));
if (NS_FAILED(rv)) return rv;
mKey = new char[aKeyLength];
if (!mKey)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mKey, aKey, aKeyLength);
mKeyLength = aKeyLength;
mRecordID = aRecordID;
mCache = aCache;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetRecordID(PRInt32 *aRecordID)
{
NS_ENSURE_ARG(aRecordID);
*aRecordID = mRecordID;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetMetaData(PRUint32 *aLength, char **aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = 0;
if (mMetaDataLength) {
*aResult = (char*)nsAllocator::Alloc(mMetaDataLength);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(*aResult, mMetaData, mMetaDataLength);
}
*aLength = mMetaDataLength;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::SetMetaData(PRUint32 aLength, const char *aData)
{
if (mMetaData)
delete[] mMetaData;
mMetaData = new char[aLength];
if (!mMetaData)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mMetaData, aData, aLength);
mMetaDataLength = aLength;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetStoredContentLength(PRUint32 *aStoredContentLength)
{
NS_ENSURE_ARG(aStoredContentLength);
return mStorageStream->GetLength(aStoredContentLength);
}
NS_IMETHODIMP
nsMemCacheRecord::SetStoredContentLength(PRUint32 aStoredContentLength)
{
PRUint32 before, after;
mStorageStream->GetLength(&before);
nsresult rv = mStorageStream->SetLength(aStoredContentLength);
if (NS_FAILED(rv)) return rv;
mStorageStream->GetLength(&after);
mCache->mOccupancy -= (before - after);
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::Delete(void)
{
if (mNumChannels)
return NS_ERROR_NOT_AVAILABLE;
return mCache->Delete(this);
}
NS_IMETHODIMP
nsMemCacheRecord::GetFilename(nsIFileSpec* *aFilename)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheRecord::NewChannel(nsILoadGroup *aLoadGroup, nsIChannel* *aResult)
{
NS_ENSURE_ARG(aResult);
nsMemCacheChannel* channel = new nsMemCacheChannel(this, aLoadGroup);
if (!channel)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(channel);
*aResult = NS_STATIC_CAST(nsIChannel*, channel);
return NS_OK;
}

View File

@@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#ifndef _nsMemCacheRecord_h_
#define _nsMemCacheRecord_h_
#include "nsINetDataCacheRecord.h"
#include "nsCOMPtr.h"
class nsMemCache;
class nsIStorageStream;
class nsMemCacheRecord : public nsINetDataCacheRecord
{
public:
// Declare interface methods
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHERECORD
protected:
// Constructors and Destructor
nsMemCacheRecord();
virtual ~nsMemCacheRecord();
nsresult Init(const char *aKey, PRUint32 aKeyLength,
PRUint32 aRecordID, nsMemCache *aCache);
char* mKey; // opaque database key for this record
PRUint32 mKeyLength; // length, in bytes, of mKey
PRInt32 mRecordID; // An alternate key for this record
char* mMetaData; // opaque URI metadata
PRUint32 mMetaDataLength; // length, in bytes, of mMetaData
nsMemCache* mCache; // weak pointer to the cache database
// that this record inhabits
nsCOMPtr<nsIStorageStream> mStorageStream;
PRUint32 mNumChannels; // Count un-Release'ed nsIChannels
friend class nsMemCache;
friend class nsMemCacheChannel;
};
#endif // _nsMemCacheRecord_h_

51
mozilla/netwerk/cache/mgr/Makefile.in vendored Normal file
View File

@@ -0,0 +1,51 @@
#
# The contents of this file are subject to the Netscape 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/NPL/
#
# 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 mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkcachemgr_s
REQUIRES = nspr
CPPSRCS = \
nsCacheManager.cpp \
nsCachedNetData.cpp \
nsReplacementPolicy.cpp \
nsCacheEntryChannel.cpp \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/../public -I$(srcdir)/../include
EXTRA_LIBS = $(NSPR_LIBS)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

45
mozilla/netwerk/cache/mgr/Makefile.win vendored Executable file
View File

@@ -0,0 +1,45 @@
#!gmake
#
# The contents of this file are subject to the Netscape 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/NPL/
#
# 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 mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkcachemgr_s
CPP_OBJS = \
.\$(OBJDIR)\nsCacheManager.obj \
.\$(OBJDIR)\nsCachedNetData.obj \
.\$(OBJDIR)\nsReplacementPolicy.obj \
.\$(OBJDIR)\nsCacheEntryChannel.obj \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -0,0 +1,260 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsCacheManager.h"
#include "nsCacheEntryChannel.h"
#include "nsIOutputStream.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIStreamListener.h"
nsCacheEntryChannel::nsCacheEntryChannel(nsCachedNetData* aCacheEntry, nsIChannel* aChannel,
nsILoadGroup* aLoadGroup):
nsChannelProxy(aChannel), mCacheEntry(aCacheEntry), mLoadGroup(aLoadGroup), mLoadAttributes(0)
{
NS_ASSERTION(aCacheEntry->mChannelCount < 0xFF, "Overflowed channel counter");
mCacheEntry->mChannelCount++;
NS_INIT_REFCNT();
}
nsCacheEntryChannel::~nsCacheEntryChannel()
{
mCacheEntry->mChannelCount--;
}
NS_IMPL_ISUPPORTS3(nsCacheEntryChannel, nsISupports, nsIChannel, nsIRequest)
// A proxy for nsIOutputStream
class CacheOutputStream : public nsIOutputStream {
public:
CacheOutputStream(nsIOutputStream *aOutputStream, nsCachedNetData *aCacheEntry):
mOutputStream(aOutputStream), mCacheEntry(aCacheEntry), mStartTime(PR_Now())
{ NS_INIT_REFCNT(); }
virtual ~CacheOutputStream() {}
NS_DECL_ISUPPORTS
NS_IMETHOD Close() {
mCacheEntry->NoteDownloadTime(mStartTime, PR_Now());
mCacheEntry->ClearFlag(nsCachedNetData::UPDATE_IN_PROGRESS);
return mOutputStream->Close();
}
NS_IMETHOD Flush() { return mOutputStream->Flush(); }
NS_IMETHOD
Write(const char *aBuf, PRUint32 aCount, PRUint32 *aActualBytes) {
nsresult rv;
*aActualBytes = 0;
rv = mOutputStream->Write(aBuf, aCount, aActualBytes);
mCacheEntry->mLogicalLength += *aActualBytes;
if (NS_FAILED(rv)) return rv;
nsCacheManager::LimitCacheSize();
return rv;
}
protected:
nsCOMPtr<nsIOutputStream> mOutputStream;
nsCOMPtr<nsCachedNetData> mCacheEntry;
// Time at which stream was opened
PRTime mStartTime;
};
NS_IMPL_ISUPPORTS(CacheOutputStream, NS_GET_IID(nsIOutputStream))
NS_IMETHODIMP
nsCacheEntryChannel::OpenOutputStream(PRUint32 aStartPosition, nsIOutputStream* *aOutputStream)
{
nsresult rv;
nsCOMPtr<nsIOutputStream> baseOutputStream;
rv = mChannel->OpenOutputStream(aStartPosition, getter_AddRefs(baseOutputStream));
if (NS_FAILED(rv)) return rv;
mCacheEntry->NoteUpdate();
mCacheEntry->NoteAccess();
mCacheEntry->mLogicalLength = aStartPosition;
*aOutputStream = new CacheOutputStream(baseOutputStream, mCacheEntry);
if (!*aOutputStream)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aOutputStream);
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aInputStream)
{
mCacheEntry->NoteAccess();
return mChannel->OpenInputStream(aStartPosition, aReadCount, aInputStream);
}
class CacheManagerStreamListener: public nsIStreamListener {
public:
CacheManagerStreamListener(nsIStreamListener *aListener,
nsILoadGroup *aLoadGroup, nsIChannel *aChannel):
mListener(aListener), mLoadGroup(aLoadGroup), mChannel(aChannel)
{ NS_INIT_REFCNT(); }
virtual ~CacheManagerStreamListener() {}
private:
NS_DECL_ISUPPORTS
NS_IMETHOD
OnDataAvailable(nsIChannel *channel, nsISupports *aContext,
nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
return mListener->OnDataAvailable(mChannel, aContext, inStr, sourceOffset, count);
}
NS_IMETHOD
OnStartRequest(nsIChannel *channel, nsISupports *aContext) {
if (mLoadGroup)
mLoadGroup->AddChannel(mChannel, aContext);
return mListener->OnStartRequest(mChannel, aContext);
}
NS_IMETHOD
OnStopRequest(nsIChannel *channel, nsISupports *aContext,
nsresult status, const PRUnichar *errorMsg) {
nsresult rv;
rv = mListener->OnStopRequest(mChannel, aContext, status, errorMsg);
if (mLoadGroup)
mLoadGroup->RemoveChannel(mChannel, aContext, status, errorMsg);
return rv;
}
private:
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIChannel> mChannel;
};
NS_IMPL_ISUPPORTS2(CacheManagerStreamListener, nsIStreamListener, nsIStreamObserver)
NS_IMETHODIMP
nsCacheEntryChannel::AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener)
{
nsresult rv;
mCacheEntry->NoteAccess();
nsCOMPtr<nsIStreamListener> headListener;
if (mLoadGroup) {
mLoadGroup->GetDefaultLoadAttributes(&mLoadAttributes);
// Create a load group "proxy" listener...
nsCOMPtr<nsILoadGroupListenerFactory> factory;
rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory));
if (NS_SUCCEEDED(rv) && factory) {
rv = factory->CreateLoadGroupListener(aListener,
getter_AddRefs(headListener));
if (NS_FAILED(rv)) return rv;
}
} else {
headListener = aListener;
}
CacheManagerStreamListener* cacheManagerStreamListener;
nsIChannel *channelForListener;
channelForListener = mProxyChannel ? mProxyChannel : this;
cacheManagerStreamListener =
new CacheManagerStreamListener(headListener, mLoadGroup, channelForListener);
if (!cacheManagerStreamListener) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(cacheManagerStreamListener);
rv = mChannel->AsyncRead(aStartPosition, aReadCount, aContext,
cacheManagerStreamListener);
NS_RELEASE(cacheManagerStreamListener);
return rv;
}
// No async writes allowed to the cache yet
NS_IMETHODIMP
nsCacheEntryChannel::AsyncWrite(nsIInputStream *aFromStream, PRUint32 aStartPosition,
PRInt32 aWriteCount, nsISupports *aContext,
nsIStreamObserver *aObserver)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
*aLoadAttributes = mLoadAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
mLoadAttributes = aLoadAttributes;
return NS_OK;
}
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
NS_IMETHODIMP
nsCacheEntryChannel::GetURI(nsIURI * *aURI)
{
char* spec;
nsresult rv;
rv = mCacheEntry->GetUriSpec(&spec);
if (NS_FAILED(rv)) return rv;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->NewURI(spec, 0, aURI);
nsAllocator::Free(spec);
return rv;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetOriginalURI(nsIURI * *aURI)
{
// FIXME - should return original URI passed into NewChannel() ?
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,82 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCacheEntryChannel_h_
#define _nsCacheEntryChannel_h_
#include "nsCOMPtr.h"
#include "nsIChannel.h"
#include "nsCachedNetData.h"
#include "nsILoadGroup.h"
class nsIStreamListener;
// A proxy for an nsIChannel, useful when only a few nsIChannel
// methods must be overridden
class nsChannelProxy : public nsIChannel {
public:
NS_FORWARD_NSICHANNEL(mChannel->)
NS_FORWARD_NSIREQUEST(mChannel->)
protected:
nsChannelProxy(nsIChannel* aChannel):mChannel(aChannel) {};
virtual ~nsChannelProxy() {};
nsCOMPtr<nsIChannel> mChannel;
};
// Override several nsIChannel methods so that they interact with the cache manager
class nsCacheEntryChannel : public nsChannelProxy {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD OpenOutputStream(PRUint32 aStartPosition, nsIOutputStream* *aOutputStream);
NS_IMETHOD OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aInputStream);
NS_IMETHOD AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener);
NS_IMETHOD AsyncWrite(nsIInputStream *aFromStream, PRUint32 aStartPosition,
PRInt32 aWriteCount, nsISupports *aContext,
nsIStreamObserver *aObserver);
NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes);
NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes);
NS_IMETHOD GetLoadGroup(nsILoadGroup* *aLoadGroup);
NS_IMETHOD GetURI(nsIURI * *aURI);
NS_IMETHOD GetOriginalURI(nsIURI * *aURI);
protected:
nsCacheEntryChannel(nsCachedNetData* aCacheEntry, nsIChannel* aChannel, nsILoadGroup* aLoadGroup);
virtual ~nsCacheEntryChannel();
friend class nsCachedNetData;
private:
nsCOMPtr<nsCachedNetData> mCacheEntry;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIChannel> mProxyChannel;
nsLoadFlags mLoadAttributes;
};
#endif // _nsCacheEntryChannel_h_

View File

@@ -0,0 +1,496 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsINetDataCache.h"
#include "nsCacheManager.h"
#include "nsCachedNetData.h"
#include "nsReplacementPolicy.h"
#include "nsString.h"
#include "nsIURI.h"
#include "nsHashtable.h"
#include "nsIComponentManager.h"
#include "nsINetDataDiskCache.h"
// Limit the number of entries in the cache to conserve memory space
// in the nsReplacementPolicy code
#define MAX_MEM_CACHE_ENTRIES 800
#define MAX_DISK_CACHE_ENTRIES 3200
// Cache capacities in MB, overridable via APIs
#define DEFAULT_MEMORY_CACHE_CAPACITY 2000
#define DEFAULT_DISK_CACHE_CAPACITY 10000
#define CACHE_HIGH_WATER_MARK(capacity) ((PRUint32)(0.98 * (capacity)))
#define CACHE_LOW_WATER_MARK(capacity) ((PRUint32)(0.97 * (capacity)))
nsCacheManager* gCacheManager = 0;
NS_IMPL_ISUPPORTS(nsCacheManager, NS_GET_IID(nsINetDataCacheManager))
nsCacheManager::nsCacheManager()
: mActiveCacheRecords(0),
mDiskCacheCapacity(DEFAULT_DISK_CACHE_CAPACITY),
mMemCacheCapacity(DEFAULT_MEMORY_CACHE_CAPACITY)
{
NS_ASSERTION(!gCacheManager, "Multiple cache managers created");
gCacheManager = this;
NS_INIT_REFCNT();
}
nsCacheManager::~nsCacheManager()
{
gCacheManager = 0;
delete mActiveCacheRecords;
delete mMemSpaceManager;
delete mDiskSpaceManager;
}
nsresult
nsCacheManager::Init()
{
nsresult rv;
mActiveCacheRecords = new nsHashtable(64);
if (!mActiveCacheRecords)
return NS_ERROR_OUT_OF_MEMORY;
// Instantiate the memory cache component
rv = nsComponentManager::CreateInstance(NS_NETWORK_MEMORY_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mMemCache));
if (NS_FAILED(rv))
return rv;
rv = nsComponentManager::CreateInstance(NS_NETWORK_FLAT_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mFlatCache));
if (NS_FAILED(rv)) {
// For now, we don't require a flat cache module to be present
if (rv != NS_ERROR_FACTORY_NOT_REGISTERED)
return rv;
}
#ifdef FILE_CACHE_IS_READY
// Instantiate the file cache component
rv = nsComponentManager::CreateInstance(NS_NETWORK_FILE_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mFileCache));
if (NS_FAILED(rv)) {
NS_WARNING("No disk cache present");
}
#endif
// Set up linked list of caches in search order
mCacheSearchChain = mMemCache;
if (mFlatCache) {
mMemCache->SetNextCache(mFlatCache);
mFlatCache->SetNextCache(mFileCache);
} else {
mMemCache->SetNextCache(mFileCache);
}
// TODO - Load any extension caches here
// Initialize replacement policy for memory cache module
mMemSpaceManager = new nsReplacementPolicy;
if (!mMemSpaceManager)
return NS_ERROR_OUT_OF_MEMORY;
rv = mMemSpaceManager->Init(MAX_MEM_CACHE_ENTRIES);
if (NS_FAILED(rv)) return rv;
rv = mMemSpaceManager->AddCache(mMemCache);
// Initialize replacement policy for disk cache modules (file
// cache and flat cache)
mDiskSpaceManager = new nsReplacementPolicy;
if (!mDiskSpaceManager)
return NS_ERROR_OUT_OF_MEMORY;
rv = mDiskSpaceManager->Init(MAX_DISK_CACHE_ENTRIES);
if (NS_FAILED(rv)) return rv;
if (mFileCache) {
rv = mDiskSpaceManager->AddCache(mFileCache);
if (NS_FAILED(rv)) return rv;
}
if (mFlatCache) {
rv = mDiskSpaceManager->AddCache(mFlatCache);
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetCachedNetData(const char *aUriSpec, const char *aSecondaryKey,
PRUint32 aSecondaryKeyLength,
PRUint32 aFlags, nsICachedNetData* *aResult)
{
nsCachedNetData *cachedData;
nsresult rv;
nsINetDataCache *cache;
nsReplacementPolicy *spaceManager;
if (aFlags & CACHE_AS_FILE) {
cache = mFileCache;
spaceManager = mDiskSpaceManager;
// Ensure that cache is initialized
if (mDiskCacheCapacity == (PRUint32)-1)
return NS_ERROR_NOT_AVAILABLE;
} else if ((aFlags & BYPASS_PERSISTENT_CACHE) || !mDiskCacheCapacity) {
cache = mMemCache;
spaceManager = mMemSpaceManager;
} else {
cache = mFlatCache ? mFlatCache : mFileCache;
spaceManager = mDiskSpaceManager;
}
// Construct the cache key by appending the secondary key to the URI spec
nsCAutoString cacheKey(aUriSpec);
// Insert NUL at end of URI spec
cacheKey += '\0';
if (aSecondaryKey)
cacheKey.Append(aSecondaryKey, aSecondaryKeyLength);
nsStringKey key(cacheKey);
cachedData = (nsCachedNetData*)mActiveCacheRecords->Get(&key);
// There is no existing instance of nsCachedNetData for this URL.
// Make one from the corresponding record in the cache module.
if (cachedData) {
NS_ASSERTION(cache == cachedData->mCache,
"Cannot yet handle simultaneously active requests for the "
"same URL using different caches");
NS_ADDREF(cachedData);
} else {
rv = spaceManager->GetCachedNetData(cacheKey.GetBuffer(), cacheKey.Length(),
cache, &cachedData);
if (NS_FAILED(rv)) return rv;
mActiveCacheRecords->Put(&key, cachedData);
}
*aResult = cachedData;
return NS_OK;
}
// Remove this cache entry from the list of active ones
nsresult
nsCacheManager::NoteDormant(nsCachedNetData* aEntry)
{
nsresult rv;
PRUint32 keyLength;
char* key;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCachedNetData* deletedEntry;
rv = aEntry->GetRecord(getter_AddRefs(record));
if (NS_FAILED(rv)) return rv;
rv = record->GetKey(&keyLength, &key);
if (NS_FAILED(rv)) return rv;
nsStringKey hashTableKey(nsCString(key, keyLength));
deletedEntry = (nsCachedNetData*)gCacheManager->mActiveCacheRecords->Remove(&hashTableKey);
NS_ASSERTION(deletedEntry == aEntry, "Hash table inconsistency");
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::Contains(const char *aUriSpec, const char *aSecondaryKey,
PRUint32 aSecondaryKeyLength,
PRUint32 aFlags, PRBool *aResult)
{
nsINetDataCache *cache;
nsReplacementPolicy *spaceManager;
nsCachedNetData *cachedData;
if (aFlags & CACHE_AS_FILE) {
cache = mFileCache;
spaceManager = mDiskSpaceManager;
} else if ((aFlags & BYPASS_PERSISTENT_CACHE) ||
(!mFileCache && !mFlatCache) || !mDiskCacheCapacity) {
cache = mMemCache;
spaceManager = mMemSpaceManager;
} else {
cache = mFlatCache ? mFlatCache : mFileCache;
spaceManager = mDiskSpaceManager;
}
// Construct the cache key by appending the secondary key to the URI spec
nsCAutoString cacheKey(aUriSpec);
// Insert NUL between URI spec and secondary key
cacheKey += '\0';
cacheKey.Append(aSecondaryKey, aSecondaryKeyLength);
// Locate the record using (URI + secondary key)
nsStringKey key(cacheKey);
cachedData = (nsCachedNetData*)mActiveCacheRecords->Get(&key);
if (cachedData && (cache == cachedData->mCache)) {
*aResult = PR_TRUE;
return NS_OK;
} else {
// No active cache entry, see if there is a dormant one
return cache->Contains(cacheKey.GetBuffer(), cacheKey.Length(), aResult);
}
}
NS_IMETHODIMP
nsCacheManager::GetNumEntries(PRUint32 *aNumEntries)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> cacheSupports;
nsCOMPtr<nsINetDataCache> cache;
PRUint32 totalEntries = 0;
rv = NewCacheModuleIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(cacheSupports));
cache = do_QueryInterface(cacheSupports);
PRUint32 numEntries;
rv = cache->GetNumEntries(&numEntries);
if (NS_FAILED(rv)) return rv;
totalEntries += numEntries;
}
*aNumEntries = totalEntries;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::NewCacheEntryIterator(nsISimpleEnumerator* *aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
class CacheEnumerator : public nsISimpleEnumerator
{
public:
CacheEnumerator(nsINetDataCache* aFirstCache):mCache(aFirstCache)
{ NS_INIT_REFCNT(); }
virtual ~CacheEnumerator() {};
NS_DECL_ISUPPORTS
NS_IMETHODIMP
HasMoreElements(PRBool* aMoreElements) {
*aMoreElements = (mCache != 0);
return NS_OK;
}
NS_IMETHODIMP
GetNext(nsISupports* *aSupports) {
*aSupports = mCache;
if (!mCache)
return NS_ERROR_FAILURE;
NS_ADDREF(*aSupports);
nsCOMPtr<nsINetDataCache> nextCache;
nsresult rv = mCache->GetNextCache(getter_AddRefs(nextCache));
mCache = nextCache;
return rv;
}
private:
nsCOMPtr<nsINetDataCache> mCache;
};
NS_IMPL_ISUPPORTS(CacheEnumerator, NS_GET_IID(nsISimpleEnumerator))
NS_IMETHODIMP
nsCacheManager::NewCacheModuleIterator(nsISimpleEnumerator* *aResult)
{
*aResult = new CacheEnumerator(mCacheSearchChain);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::RemoveAll(void)
{
nsresult rv, result;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsINetDataCache> cache;
nsCOMPtr<nsISupports> iSupports;
result = NS_OK;
rv = NewCacheModuleIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(iSupports));
cache = do_QueryInterface(iSupports);
PRUint32 cacheFlags;
rv = cache->GetFlags(&cacheFlags);
if (NS_FAILED(rv)) return rv;
if ((cacheFlags & nsINetDataCache::READ_ONLY) == 0) {
rv = cache->RemoveAll();
if (NS_FAILED(rv))
result = rv;
}
}
return result;
}
nsresult
nsCacheManager::LimitMemCacheSize()
{
nsresult rv;
nsReplacementPolicy* spaceManager;
NS_ASSERTION(gCacheManager, "No cache manager");
spaceManager = gCacheManager->mMemSpaceManager;
PRUint32 occupancy;
rv = spaceManager->GetStorageInUse(&occupancy);
if (NS_FAILED(rv)) return rv;
PRUint32 memCacheCapacity = gCacheManager->mMemCacheCapacity;
if (occupancy > CACHE_HIGH_WATER_MARK(memCacheCapacity))
return spaceManager->Evict(CACHE_LOW_WATER_MARK(memCacheCapacity));
return NS_OK;
}
nsresult
nsCacheManager::LimitDiskCacheSize()
{
nsresult rv;
nsReplacementPolicy* spaceManager;
NS_ASSERTION(gCacheManager, "No cache manager");
spaceManager = gCacheManager->mDiskSpaceManager;
PRUint32 occupancy;
rv = spaceManager->GetStorageInUse(&occupancy);
if (NS_FAILED(rv)) return rv;
PRUint32 diskCacheCapacity = gCacheManager->mDiskCacheCapacity;
if (occupancy > CACHE_HIGH_WATER_MARK(diskCacheCapacity))
return spaceManager->Evict(CACHE_LOW_WATER_MARK(diskCacheCapacity));
return NS_OK;
}
nsresult
nsCacheManager::LimitCacheSize()
{
nsresult rv;
rv = LimitDiskCacheSize();
if (NS_FAILED(rv)) return rv;
rv = LimitMemCacheSize();
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetMemCacheCapacity(PRUint32 aCapacity)
{
mMemCacheCapacity = aCapacity;
LimitCacheSize();
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetMemCacheCapacity(PRUint32* aCapacity)
{
NS_ENSURE_ARG_POINTER(aCapacity);
*aCapacity = mMemCacheCapacity;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetDiskCacheCapacity(PRUint32 aCapacity)
{
mDiskCacheCapacity = aCapacity;
LimitCacheSize();
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetDiskCacheCapacity(PRUint32* aCapacity)
{
NS_ENSURE_ARG_POINTER(aCapacity);
*aCapacity = mDiskCacheCapacity;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetDiskCacheFolder(nsIFileSpec* aFolder)
{
NS_ENSURE_ARG(aFolder);
if (!mFileCache)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsINetDataDiskCache> fileCache;
fileCache = do_QueryInterface(mFileCache);
return fileCache->SetDiskCacheFolder(aFolder);
}
NS_IMETHODIMP
nsCacheManager::GetDiskCacheFolder(nsIFileSpec* *aFolder)
{
NS_ENSURE_ARG(aFolder);
if (!mFileCache)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsINetDataDiskCache> fileCache;
fileCache = do_QueryInterface(mFileCache);
return fileCache->GetDiskCacheFolder(aFolder);
}

View File

@@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCacheManager_h_
#define _nsCacheManager_h_
// 2030f0b0-9567-11d3-90d3-0040056a906e
#define NS_CACHE_MANAGER_CID \
{ \
0x2030f0b0, \
0x9567, \
0x11d3, \
{0x90, 0xd3, 0x00, 0x40, 0x05, 0x6a, 0x90, 0x6e} \
}
#include "nsINetDataCacheManager.h"
#include "nsCOMPtr.h"
class nsHashtable;
class nsReplacementPolicy;
class nsCachedNetData;
class nsCacheManager : public nsINetDataCacheManager {
public:
nsCacheManager();
virtual ~nsCacheManager();
NS_METHOD Init();
// nsISupports methods
NS_DECL_ISUPPORTS
// nsINetDataCacheManager methods
NS_DECL_NSINETDATACACHEMANAGER
private:
// Mapping from cache key to nsCachedNetData, but only for those cache
// entries with external references, i.e. those referred to outside the
// cache manager
nsHashtable* mActiveCacheRecords;
// Memory cache
nsCOMPtr<nsINetDataCache> mMemCache;
// Flat-file database cache; All content aggregated into single disk file
nsCOMPtr<nsINetDataCache> mFlatCache;
// stream-as-file cache
nsCOMPtr<nsINetDataCache> mFileCache;
// Unified replacement policy for flat-cache and file-cache
nsReplacementPolicy* mDiskSpaceManager;
// Replacement policy for memory cache
nsReplacementPolicy* mMemSpaceManager;
// List of caches in search order
nsINetDataCache* mCacheSearchChain;
// Combined file/flat cache capacity, in KB
PRUint32 mDiskCacheCapacity;
// Memory cache capacity, in KB
PRUint32 mMemCacheCapacity;
protected:
static nsresult NoteDormant(nsCachedNetData* aEntry);
static nsresult LimitCacheSize();
static nsresult LimitMemCacheSize();
static nsresult LimitDiskCacheSize();
friend class nsCachedNetData;
friend class CacheOutputStream;
};
#endif // _nsCacheManager_h_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,242 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCachedNetData_h_
#define _nsCachedNetData_h_
#include "nsICachedNetData.h"
#include "nsCOMPtr.h"
#include "nsINetDataCacheRecord.h"
class nsINetDataCache;
class nsIStreamAsFileObserver;
class nsIStreamAsFile;
class nsIArena;
class StreamAsFileObserverClosure;
class CacheMetaData;
// Number of recent access times recorded
#define MAX_K 3
/**
* FIXME - add comment. There are a lot of these data structures resident in
* memory, so be careful about adding members unnecessarily.
*/
class nsCachedNetData : public nsICachedNetData {
public:
NS_DECL_ISUPPORTS
// nsICachedNetData methods
NS_DECL_NSICACHEDNETDATA
NS_METHOD Init(nsINetDataCacheRecord *aRecord, nsINetDataCache *aCache);
protected:
// Bits for mFlags, below
typedef enum {
DIRTY = 1 << 0, // Cache entry data needs to be flushed to database
// ==== Flags that can be set by the protocol handler ====
ALLOW_PARTIAL = 1 << 1, // Protocol handler supports partial fetching
UPDATE_IN_PROGRESS = 1 << 2, // Protocol handler now modifying cache data
// ==== Cache-entry state flags. At most one of these flags can be set ====
TRUNCATED_CONTENT = 1 << 4, // Entry contains valid content, but it has
// been truncated by cache manager
// A previously-used cache entry, which has been purged of all cached
// content and protocol-private data. This cache entry can be refilled
// with new content or it may be retained in this vestigial state
// because the usage statistics it contains will be used by the
// replacement policy if the same URI is ever cached again.
VESTIGIAL = 1 << 5,
// ==== Memory usage status bits. At most one of these flags can be set ====
RECYCLED = 1 << 8, // Previously associated database record has
// been deleted; This cache entry is available
// for recycling.
DORMANT = 1 << 9, // No references to this cache entry, except by
// the cache manager itself
// ==== Setter bits ====
LAST_MODIFIED_KNOWN = 1 <<12, // Protocol handler called SetLastModifiedTime()
EXPIRATION_KNOWN = 1 <<13, // Protocol handler called SetExpirationTime()
STALE_TIME_KNOWN = 1 <<14, // Protocol handler called SetStaleTime()
// ==== Useful flag combinations ====
// Cache entry not eligible for eviction
UNEVICTABLE = VESTIGIAL | RECYCLED | UPDATE_IN_PROGRESS,
// State flags that are in-memory only, i.e. not persistent
TRANSIENT_FLAGS = DIRTY | RECYCLED | DORMANT
} Flag;
PRBool GetFlag(Flag aFlag) { return (mFlags & aFlag) != 0; }
nsresult GetFlag(PRBool *aResult, Flag aFlag) { *aResult = GetFlag(aFlag); return NS_OK; }
// Set a boolean flag for the cache entry
nsresult SetFlag(PRBool aValue, Flag aFlag);
nsresult SetFlag(Flag aFlag) { return SetFlag(PR_TRUE, aFlag); }
nsresult ClearFlag(Flag aFlag) { return SetFlag(PR_FALSE, aFlag); }
void ComputeProfit(PRUint32 aCurrentTime);
static int Compare(const void *a, const void *b, void *unused);
void NoteAccess();
void NoteUpdate();
// Get underlying raw cache database record.
nsresult GetRecord(nsINetDataCacheRecord* *aRecord);
nsresult GetRecordID(PRInt32 *aRecordID);
nsresult Evict(PRUint32 aTruncatedContentLength);
nsresult GetFileSpec(nsIFileSpec* *aFileSpec);
void NoteDownloadTime(PRTime start, PRTime end);
// placement new for arena-allocation
void *operator new (size_t aSize, nsIArena *aArena);
friend class nsReplacementPolicy;
friend class nsCacheManager;
friend class StreamAsFile;
friend class nsCacheEntryChannel;
friend class CacheOutputStream;
friend class InterceptStreamListener;
private:
nsCachedNetData() {};
virtual ~nsCachedNetData() {};
// Initialize internal fields of this nsCachedNetData instance from the
// underlying raw cache database record.
nsresult Deserialize(bool aDeserializeFlags);
// Notify stream-as-file observers about change in cache entry status
nsresult Notify(PRUint32 aMessage, nsresult aError);
// Add/Remove stream-as-file observers
nsresult AddObserver(nsIStreamAsFile *aStreamAsFile, nsIStreamAsFileObserver* aObserver);
nsresult RemoveObserver(nsIStreamAsFileObserver* aObserver);
// Mark cache entry to indicate a write out to the cache database is required
void SetDirty() { mFlags |= DIRTY; }
nsresult Resurrect(nsINetDataCacheRecord *aRecord);
nsresult CommitFlags();
CacheMetaData* FindTaggedMetaData(const char* aTag, PRBool aCreate);
private:
// List of nsIStreamAsFileObserver's that will receive notification events
// when the cache manager or a client desires to delete/truncate a cache
// entry file.
StreamAsFileObserverClosure* mObservers;
// Protocol-specific meta-data, opaque to the cache manager
CacheMetaData *mMetaData;
// Next in chain for a single bucket in the replacement policy hash table
// that maps from record ID to nsCachedNetData
nsCachedNetData* mNext;
// See flag bits, above
// NOTE: 16 bit member is combined with members below for
// struct packing efficiency. Do not change order of members!
PRUint16 mFlags;
protected:
// Number of nsCacheEntryChannels referring to this record
PRUint8 mChannelCount;
// Below members are statistics kept per cache-entry, used to decide how
// profitable it will be to evict a record from the cache relative to other
// existing records. Note: times are measured in *seconds* since the
// 1/1/70 epoch, same as a unix time_t.
// Number of accesses for this cache record
// NOTE: 8 bit member is combined with members above for
// struct packing efficiency. Do not change order of members!
PRUint8 mNumAccesses;
// A reference to the underlying, raw cache database record, either as a
// pointer to an in-memory object or as a database record identifier
union {
nsINetDataCacheRecord* mRecord;
// Database record ID of associated cache record. See
// nsINetDataCache::GetRecordByID().
PRInt32 mRecordID;
};
// Weak link to parent cache
nsINetDataCache* mCache;
// Length of stored content, which may be less than storage consumed if
// compression is used
PRUint32 mLogicalLength;
// Most recent cache entry access times, used to compute access frequency
PRUint32 mAccessTime[MAX_K];
// We use modification time of the original document for replacement policy
// computations, i.e. to compute a document's age, but if we don't know it,
// we use the time that the document was last written to the cache.
union {
// Document modification time, if known.
PRUint32 mLastModifiedTime;
// Time of last cache update for this doc
PRUint32 mLastUpdateTime;
};
union {
// Time until which document is fresh, i.e. does not have to be validated
// with server and, therefore, data in cache is guaranteed usable
PRUint32 mExpirationTime;
// Heuristic time at which cached document is likely to be out-of-date
// with respect to canonical copy on server. Used for cache replacement
// policy, not for validation.
PRUint32 mStaleTime;
};
// Download time per byte, measure roughly in units of KB/s
float mDownloadRate;
// Heuristic estimate of cache entry future benefits, based on above values
float mProfit;
};
#endif // _nsCachedNetData_h_

View File

@@ -0,0 +1,658 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsReplacementPolicy.h"
#include "nsCachedNetData.h"
#include "nsQuickSort.h"
#include "nsIAllocator.h"
#include "nsIEnumerator.h"
#include "prtime.h"
#include "prbit.h"
#include "nsCOMPtr.h"
#include <math.h>
// Constant used to estimate frequency of access to a document based on size
#define CACHE_CONST_B 1.35
// A cache whose space is managed by this replacement policy
class nsReplacementPolicy::CacheInfo {
public:
CacheInfo(nsINetDataCache* aCache):mCache(aCache),mNext(0) {}
nsINetDataCache* mCache;
CacheInfo* mNext;
};
nsReplacementPolicy::nsReplacementPolicy()
: mRankedEntries(0), mCaches(0), mRecordsRemovedSinceLastRanking(0),
mNumEntries(0), mCapacityRankedEntriesArray(0), mLastRankTime(0) {}
nsReplacementPolicy::~nsReplacementPolicy()
{
if (mRankedEntries)
nsAllocator::Free(mRankedEntries);
if (mMapRecordIdToEntry)
nsAllocator::Free(mMapRecordIdToEntry);
}
nsresult
nsReplacementPolicy::Init(PRUint32 aMaxCacheEntries)
{
nsresult rv;
rv = NS_NewHeapArena(getter_AddRefs(mArena), sizeof(nsCachedNetData) * 32);
if (NS_FAILED(rv)) return rv;
mMaxEntries = aMaxCacheEntries;
mHashArrayLength = PR_CeilingLog2(aMaxCacheEntries) >> 3;
size_t numBytes = mHashArrayLength * sizeof(*mMapRecordIdToEntry);
mMapRecordIdToEntry = (nsCachedNetData**)nsAllocator::Alloc(numBytes);
if (!mMapRecordIdToEntry)
return NS_ERROR_OUT_OF_MEMORY;
nsCRT::zero(mMapRecordIdToEntry, numBytes);
return NS_OK;
}
nsresult
nsReplacementPolicy::AddCache(nsINetDataCache *aCache)
{
CacheInfo *cacheInfo = new CacheInfo(aCache);
if (!cacheInfo)
return NS_ERROR_OUT_OF_MEMORY;
cacheInfo->mNext = mCaches;
mCaches = cacheInfo;
return NS_OK;
}
PRUint32
nsReplacementPolicy::HashRecordID(PRInt32 aRecordID)
{
return ((aRecordID >> 16) ^ aRecordID) & (mHashArrayLength - 1);
}
nsCachedNetData*
nsReplacementPolicy::FindCacheEntryByRecordID(PRInt32 aRecordID, nsINetDataCache *aCache)
{
nsresult rv;
nsCachedNetData* cacheEntry;
PRUint32 bucket = HashRecordID(aRecordID);
cacheEntry = mMapRecordIdToEntry[bucket];
for (;cacheEntry; cacheEntry = cacheEntry->mNext) {
PRInt32 recordID;
rv = cacheEntry->GetRecordID(&recordID);
if (NS_FAILED(rv))
continue;
if ((recordID == aRecordID) && (cacheEntry->mCache == aCache))
return cacheEntry;
}
return 0;
}
// Add a cache entry to the hash table that maps record ID to entries
void
nsReplacementPolicy::AddCacheEntry(nsCachedNetData* aCacheEntry, PRInt32 aRecordID)
{
nsCachedNetData** cacheEntryp;
PRUint32 bucket = HashRecordID(aRecordID);
cacheEntryp = &mMapRecordIdToEntry[bucket];
while (*cacheEntryp)
cacheEntryp = &(*cacheEntryp)->mNext;
*cacheEntryp = aCacheEntry;
aCacheEntry->mNext = 0;
}
// Delete a cache entry from the hash table that maps record ID to entries
nsresult
nsReplacementPolicy::DeleteCacheEntry(nsCachedNetData* aCacheEntry)
{
nsresult rv;
PRInt32 recordID;
rv = aCacheEntry->GetRecordID(&recordID);
if (NS_FAILED(rv)) return rv;
PRUint32 bucket = HashRecordID(recordID);
nsCachedNetData** cacheEntryp;
cacheEntryp = &mMapRecordIdToEntry[bucket];
while (*cacheEntryp) {
if (*cacheEntryp == aCacheEntry) {
*cacheEntryp = aCacheEntry->mNext;
return NS_OK;
}
cacheEntryp = &(*cacheEntryp)->mNext;
}
NS_ASSERTION(0, "hash table inconsistency");
return NS_ERROR_FAILURE;
}
nsresult
nsReplacementPolicy::AddAllRecordsInCache(nsINetDataCache *aCache)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> iSupports;
nsCOMPtr<nsINetDataCacheRecord> record;
rv = aCache->NewCacheEntryIterator(getter_AddRefs(iterator));
if (!NS_SUCCEEDED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
rv = iterator->GetNext(getter_AddRefs(iSupports));
if (!NS_SUCCEEDED(rv)) return rv;
record = do_QueryInterface(iSupports);
rv = AssociateCacheEntryWithRecord(record, aCache, 0);
if (!NS_SUCCEEDED(rv)) return rv;
}
return NS_OK;
}
// Get current time and convert to seconds since the epoch
static PRUint32
now32()
{
double nowFP;
PRInt64 now64 = PR_Now();
LL_L2D(nowFP, now64);
PRUint32 now = (PRUint32)(nowFP * 1e-6);
return now;
}
void
nsCachedNetData::NoteDownloadTime(PRTime start, PRTime end)
{
double startFP, endFP, rate, duration;
LL_L2D(startFP, start);
LL_L2D(endFP, end);
duration = endFP - startFP;
// Sanity-check
if (!duration)
return;
// Compute download rate in kB/s
rate = mLogicalLength / (duration * (1e-6 /1024.0));
// Exponentially smooth download rate
const double alpha = 0.5;
mDownloadRate = (float)(mDownloadRate * alpha + rate * (1.0 - alpha));
}
// 1 hour
#define MIN_HALFLIFE (60 * 60)
// 1 week
#define TYPICAL_HALFLIFE (7 * 24 * 60 * 60)
/**
* Estimate the profit that would be lost if the given cache entry was evicted
* from the cache. Profit is defined as the future expected download delay per
* byte of cached content. The profit computation is made based on projected
* frequency of access, prior download performance and a heuristic staleness
* criteria. The technique used is a variation of that described in the
* following paper:
*
* "A Case for Delay-Conscious Caching of Web Documents"
* http://www.bell-labs.com/user/rvingral/www97.html
*
* Briefly, expected profit is:
*
* (projected frequency of access) * (download time per byte) * (probability freshness)
*/
void
nsCachedNetData::ComputeProfit(PRUint32 aNow)
{
PRUint32 K, now;
if (aNow)
now = aNow;
else
now = now32();
K = PR_MIN(MAX_K, mNumAccesses);
if (!K) {
mProfit = 0;
return;
}
// Compute time, in seconds, since k'th most recent access
double timeSinceKthAccess = now - mAccessTime[K - 1];
if (timeSinceKthAccess <= 0.0) // Sanity check
timeSinceKthAccess = 1.0;
// Estimate frequency of future document access based on past
// access frequency
double frequencyAccess = K / timeSinceKthAccess;
// If we don't have much historical data on access frequency
// use a heuristic based on document size as an estimate
if (mLogicalLength) {
if (K == 1) {
frequencyAccess /= pow(mLogicalLength, CACHE_CONST_B);
} else if (K == 2) {
frequencyAccess /= pow(mLogicalLength, CACHE_CONST_B / 2);
}
}
// Estimate likelihood that data in cache is fresh, i.e.
// that it corresponds to the document on the server
double probabilityFreshness;
PRInt32 halfLife, age, docTime;
bool potentiallyStale;
docTime = GetFlag(LAST_MODIFIED_KNOWN) ? mLastModifiedTime : mLastUpdateTime;
age = now - docTime;
probabilityFreshness = 1.0; // Optimistic
if (GetFlag(EXPIRATION_KNOWN)) {
potentiallyStale = now > mExpirationTime;
halfLife = mExpirationTime - mLastModifiedTime;
} else if (GetFlag(STALE_TIME_KNOWN)) {
potentiallyStale = true;
halfLife = mStaleTime - docTime;
} else {
potentiallyStale = true;
halfLife = TYPICAL_HALFLIFE;
}
if (potentiallyStale) {
if (halfLife < MIN_HALFLIFE)
halfLife = MIN_HALFLIFE;
probabilityFreshness = pow(0.5, (double)age / (double)halfLife);
}
mProfit = (float)(frequencyAccess * probabilityFreshness * mDownloadRate);
}
// Number of entries to grow mRankedEntries array when it's full
#define STATS_GROWTH_INCREMENT 256
// Sorting predicate for NS_Quicksort
int
nsCachedNetData::Compare(const void *a, const void *b, void *unused)
{
nsCachedNetData* entryA = (nsCachedNetData*)a;
nsCachedNetData* entryB = (nsCachedNetData*)b;
// Percolate deleted or empty entries to the end of the mRankedEntries
// array, so that they can be recycled.
if (!entryA || entryA->GetFlag(RECYCLED)) {
if (!entryB || entryB->GetFlag(RECYCLED))
return 0;
else
return +1;
}
if (!entryB || entryB->GetFlag(RECYCLED))
return -1;
// Evicted entries (those with no content data) and active entries (those
// currently being updated) are collected towards the end of the sorted
// array just prior to the deleted cache entries, since evicted entries
// can't be re-evicted.
if (entryA->GetFlag(UPDATE_IN_PROGRESS)) {
if (entryB->GetFlag(UPDATE_IN_PROGRESS))
return 0;
else
return +1;
}
if (entryB->GetFlag(UPDATE_IN_PROGRESS))
return -1;
PRUint16 Ka = PR_MIN(MAX_K, entryA->mNumAccesses);
PRUint16 Kb = PR_MIN(MAX_K, entryB->mNumAccesses);
// Order cache entries by the number of times they've been accessed
if (Ka < Kb)
return -1;
if (Ka > Kb)
return +1;
/*
* Among records that have been accessed an equal number of times, order
* them by profit.
*/
if (entryA->mProfit > entryB->mProfit)
return +1;
if (entryA->mProfit < entryB->mProfit)
return -1;
return 0;
}
/**
* Rank cache entries in terms of their elegibility for eviction.
*/
nsresult
nsReplacementPolicy::RankRecords()
{
PRUint32 i, now;
// Add all cache records if this is the first ranking
if (!mLastRankTime) {
nsresult rv;
CacheInfo *cacheInfo;
cacheInfo = mCaches;
while (cacheInfo) {
rv = AddAllRecordsInCache(cacheInfo->mCache);
if (NS_FAILED(rv)) return rv;
cacheInfo = cacheInfo->mNext;
}
}
// Get current time and convert to seconds since the epoch
now = now32();
// Recompute profit for every known cache record, except deleted ones
for (i = 0; i < mNumEntries; i++) {
nsCachedNetData* entry = mRankedEntries[i];
if (entry && !entry->GetFlag(nsCachedNetData::RECYCLED))
entry->ComputeProfit(now);
}
NS_QuickSort(mRankedEntries, mNumEntries, sizeof *mRankedEntries,
nsCachedNetData::Compare, 0);
mNumEntries -= mRecordsRemovedSinceLastRanking;
mRecordsRemovedSinceLastRanking = 0;
mLastRankTime = now;
return NS_OK;
}
// A heuristic policy to avoid the cost of re-ranking cache records by
// profitability every single time space must be made available in the cache.
void
nsReplacementPolicy::MaybeRerankRecords()
{
// Rank at most once per minute
PRUint32 now = now32();
if ((now - mLastRankTime) >= 60)
RankRecords();
}
void
nsReplacementPolicy::CompactRankedEntriesArray()
{
if (mRecordsRemovedSinceLastRanking || !mLastRankTime)
RankRecords();
}
nsresult
nsReplacementPolicy::CheckForTooManyCacheEntries()
{
if (mCapacityRankedEntriesArray == mMaxEntries) {
return DeleteOneEntry(0);
} else {
nsresult rv;
CacheInfo *cacheInfo;
cacheInfo = mCaches;
while (cacheInfo) {
PRUint32 numEntries, maxEntries;
rv = cacheInfo->mCache->GetNumEntries(&numEntries);
if (NS_FAILED(rv)) return rv;
rv = cacheInfo->mCache->GetMaxEntries(&maxEntries);
if (NS_FAILED(rv)) return rv;
if (numEntries == maxEntries)
return DeleteOneEntry(cacheInfo->mCache);
cacheInfo = cacheInfo->mNext;
}
}
return NS_OK;
}
/**
* Create a new association between a low-level cache database record and a
* cache entry. Add the entry to the set of entries eligible for eviction from
* the cache. This would typically be done when the cache entry is created.
*/
nsresult
nsReplacementPolicy::AssociateCacheEntryWithRecord(nsINetDataCacheRecord *aRecord,
nsINetDataCache* aCache,
nsCachedNetData** aResult)
{
nsCachedNetData* cacheEntry;
nsresult rv;
// First, see if the record is already known to the replacement policy
PRInt32 recordID;
rv = aRecord->GetRecordID(&recordID);
if (NS_FAILED(rv)) return rv;
cacheEntry = FindCacheEntryByRecordID(recordID, aCache);
if (cacheEntry) {
if (aResult) {
if (cacheEntry->GetFlag(nsCachedNetData::DORMANT))
cacheEntry->Resurrect(aRecord);
NS_ADDREF(cacheEntry);
*aResult = cacheEntry;
}
return NS_OK;
}
// Compact the array of cache entry statistics, so that free entries appear
// at the end, for possible reuse.
if (mNumEntries && (mNumEntries == mCapacityRankedEntriesArray))
CompactRankedEntriesArray();
// If compaction doesn't yield available entries in the
// mRankedEntries array, then extend the array.
if (mNumEntries == mCapacityRankedEntriesArray) {
PRUint32 newCapacity;
rv = CheckForTooManyCacheEntries();
if (NS_FAILED(rv)) return rv;
newCapacity = mCapacityRankedEntriesArray + STATS_GROWTH_INCREMENT;
if (newCapacity > mMaxEntries)
newCapacity = mMaxEntries;
nsCachedNetData** newRankedEntriesArray;
PRUint32 numBytes = sizeof(nsCachedNetData*) * newCapacity;
newRankedEntriesArray =
(nsCachedNetData**)nsAllocator::Realloc(mRankedEntries, numBytes);
if (!newRankedEntriesArray)
return NS_ERROR_OUT_OF_MEMORY;
mRankedEntries = newRankedEntriesArray;
mCapacityRankedEntriesArray = newCapacity;
PRUint32 i;
for (i = mNumEntries; i < newCapacity; i++)
mRankedEntries[i] = 0;
}
// Recycle the record after the last in-use record in the array
nsCachedNetData *entry = mRankedEntries[mNumEntries];
NS_ASSERTION(!entry || !entry->GetFlag(nsCachedNetData::RECYCLED),
"Only deleted cache entries should appear at end of array");
if (!entry) {
entry = new(mArena) nsCachedNetData;
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
mRankedEntries[mNumEntries] = entry;
} else {
// Clear out recycled data structure
nsCRT::zero(entry, sizeof(*entry));
}
entry->Init(aRecord, aCache);
AddCacheEntry(entry, recordID);
// Add one reference to the cache entry from the cache manager
NS_ADDREF(entry);
if (aResult) {
// And one reference from our caller
NS_ADDREF(entry);
*aResult = entry;
}
mNumEntries++;
return NS_OK;
}
nsresult
nsReplacementPolicy::GetCachedNetData(const char* cacheKey, PRUint32 cacheKeyLength,
nsINetDataCache* aCache,
nsCachedNetData** aResult)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
rv = aCache->GetCachedNetData(cacheKey, cacheKeyLength,
getter_AddRefs(record));
if (NS_FAILED(rv)) return rv;
return AssociateCacheEntryWithRecord(record, aCache, aResult);
}
/**
* Delete the least desirable record from the cache database. This is used
* when the addition of another record would exceed either the cache manager or
* the cache's maximum permitted number of records.
*/
nsresult
nsReplacementPolicy::DeleteOneEntry(nsINetDataCache *aCache)
{
PRUint32 i;
nsresult rv;
nsCachedNetData *entry;
i = 0;
while (1) {
MaybeRerankRecords();
for (; i < mNumEntries; i++) {
entry = mRankedEntries[i];
if (!entry || entry->GetFlag(nsCachedNetData::RECYCLED))
continue;
if (!aCache || (entry->mCache == aCache))
break;
}
// Report error if no record found to delete
if (i == mNumEntries)
return NS_ERROR_FAILURE;
rv = entry->Delete();
if (NS_SUCCEEDED(rv)) {
rv = DeleteCacheEntry(entry);
return rv;
}
}
}
nsresult
nsReplacementPolicy::GetStorageInUse(PRUint32* aStorageInUse)
{
nsresult rv;
CacheInfo *cacheInfo;
*aStorageInUse = 0;
cacheInfo = mCaches;
while (cacheInfo) {
PRUint32 cacheStorage;
rv = cacheInfo->mCache->GetStorageInUse(&cacheStorage);
if (NS_FAILED(rv)) return rv;
*aStorageInUse += cacheStorage;
cacheInfo = cacheInfo->mNext;
}
return NS_OK;
}
/**
* Delete the least desirable records from the cache until the occupancy of the
* cache has been reduced by the given number of KB. This is used when the
* addition of more cache data would exceed the cache's capacity.
*/
nsresult
nsReplacementPolicy::Evict(PRUint32 aTargetOccupancy)
{
PRUint32 i;
nsCachedNetData *entry;
nsresult rv;
PRUint32 occupancy;
PRInt32 truncatedLength;
nsCOMPtr<nsINetDataCacheRecord> record;
MaybeRerankRecords();
for (i = 0; i < mNumEntries; i++) {
rv = GetStorageInUse(&occupancy);
if (!NS_SUCCEEDED(rv)) return rv;
if (occupancy <= aTargetOccupancy)
return NS_OK;
entry = mRankedEntries[i];
// Skip deleted/empty cache entries and ones that have already been evicted
if (!entry || entry->GetFlag(nsCachedNetData::UNEVICTABLE))
continue;
if (entry->GetFlag(nsCachedNetData::ALLOW_PARTIAL)) {
rv = entry->GetRecord(getter_AddRefs(record));
if (NS_FAILED(rv))
continue;
PRUint32 contentLength;
rv = record->GetStoredContentLength(&contentLength);
if (NS_FAILED(rv))
continue;
// Additional cache storage required, in KB
PRUint32 storageToReclaim = (occupancy - aTargetOccupancy) << 10;
truncatedLength = (PRInt32)(contentLength - storageToReclaim);
if (truncatedLength < 0)
truncatedLength = 0;
} else {
truncatedLength = 0;
}
rv = entry->Evict(truncatedLength);
}
return NS_ERROR_FAILURE;
}

View File

@@ -0,0 +1,136 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
/**
* This class manages one or more caches that share a storage resource, e.g. a
* file cache and a flat-database cache might each occupy space on the disk and
* they would share a single instance of nsReplacementPolicy. The replacement
* policy heuristically chooses which cache entries to evict when storage is
* required to accommodate incoming cache data.
*/
#ifndef _nsReplacementPolicy_h_
#define _nsReplacementPolicy_h_
#include "nscore.h"
#include "nsISupportsUtils.h"
#include "nsINetDataCache.h"
#include "nsICachedNetData.h"
#include "nsIArena.h"
#include "nsCOMPtr.h"
#include "nsHashtable.h"
class nsCachedNetData;
struct PL_HashTable;
/**
* This private class is responsible for implementing the network data cache's
* replacement policy, i.e. it decides which cache data should be evicted to
* make room for new incoming data.
*/
class nsReplacementPolicy {
public:
nsReplacementPolicy();
~nsReplacementPolicy();
protected:
nsresult Init(PRUint32 aMaxCacheEntries);
nsresult AddCache(nsINetDataCache *aCache);
nsresult GetCachedNetData(const char* cacheKey, PRUint32 cacheKeyLength,
nsINetDataCache* aCache,
nsCachedNetData** aResult);
nsresult GetStorageInUse(PRUint32* aNumKBytes);
friend class nsCacheManager;
private:
nsresult RankRecords();
void MaybeRerankRecords();
void CompactRankedEntriesArray();
nsresult DeleteOneEntry(nsINetDataCache* aCache);
nsresult Evict(PRUint32 aTargetOccupancy);
nsCachedNetData* FindCacheEntryByRecordID(PRInt32 aRecordID, nsINetDataCache *aCache);
void AddCacheEntry(nsCachedNetData* aCacheEntry, PRInt32 aRecordID);
nsresult DeleteCacheEntry(nsCachedNetData* aCacheEntry);
PRUint32 HashRecordID(PRInt32 aRecordID);
nsresult AssociateCacheEntryWithRecord(nsINetDataCacheRecord *aRecord,
nsINetDataCache* aCache,
nsCachedNetData** aResult);
nsresult AddAllRecordsInCache(nsINetDataCache *aCache);
nsresult CheckForTooManyCacheEntries();
class CacheInfo;
private:
// Growable array of pointers to individual cache entries; It is sorted by
// profitability, such that low-numbered array indices refer to cache
// entries that are the least profitable to retain. New cache entries are
// added to the end of the array. Deleted cache entries are specially
// marked and percolate to the end of the array for recycling whenever
// mRankedEntries is sorted. Evicted cache entries (those with no
// associated content data) are retained for the purpose of improving the
// replacement policy efficacy, and are percolated towards the end of the
// array, just prior to the deleted cache entries.
//
// The array is not in sorted order 100% of the time; For efficiency
// reasons, sorting is only done when heuristically deemed necessary.
nsCachedNetData** mRankedEntries;
// Hash table buckets to map Record ID to cache entry. We use this instead
// of a PL_HashTable to reduce storage requirements
nsCachedNetData** mMapRecordIdToEntry;
// Length of mMapRecordIdToEntry array
PRUint32 mHashArrayLength;
// Linked list of caches that share this replacement policy
CacheInfo* mCaches;
// Allocation area for cache entry (nsCachedNetData) instances
nsCOMPtr<nsIArena> mArena;
// Bookkeeping
PRUint32 mRecordsRemovedSinceLastRanking;
// Maximum permitted length of mRankedEntries array
PRUint32 mMaxEntries;
// Number of occupied slots in mRankedEntries array
PRUint32 mNumEntries;
// Capacity of mRankedEntries array
PRUint32 mCapacityRankedEntriesArray;
// Time at which cache entries were last ranked by profitability
PRUint32 mLastRankTime;
};
#endif // _nsReplacementPolicy_h_

View File

@@ -0,0 +1,39 @@
#
# The contents of this file are subject to the Netscape 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/NPL/
#
# 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 mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
EXPORTS = \
nsICacheManager.h \
nsICacheObject.h \
nsICachePref.h \
nsICacheModule.h \
$(NULL)
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))
include $(topsrcdir)/config/rules.mk

41
mozilla/netwerk/cache/public/Makefile.win vendored Executable file
View File

@@ -0,0 +1,41 @@
#!gmake
#
# The contents of this file are subject to the Netscape 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/NPL/
#
# 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 mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
MODULE = nkcache
DEPTH = ..\..\..
include <$(DEPTH)/config/config.mak>
EXPORTS = \
$(NULL)
XPIDLSRCS = \
.\nsICachedNetData.idl \
.\nsINetDataCacheManager.idl \
.\nsINetDataCache.idl \
.\nsINetDataCacheRecord.idl \
.\nsINetDataDiskCache.idl \
.\nsIStreamAsFile.idl \
$(NULL)
include <$(DEPTH)/config/rules.mak>

View File

@@ -0,0 +1,229 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsrootidl.idl"
#include "nsISupports.idl"
interface nsIFileSpec;
interface nsIURI;
interface nsIObserver;
interface nsIChannel;
interface nsINetDataCache;
interface nsINetDataCacheRecord;
interface nsILoadGroup;
interface nsIStreamListener;
/**
* The nsICachedNetData interface represents a single entry in a database that
* caches data retrieved from the network. This interface is implemented by the
* cache manager on top of the low-level nsINetDataCacheRecord and
* nsINetDataCache interfaces that are implemented by the database.
*
* Each cache record may contain both content and metadata. The content may
* be, for example, GIF image data or HTML, and it is accessed through
* nsIChannel's streaming API. The opaque metadata, which may contain HTTP
* headers among other things, is stored as a byte array. Each entry in the
* cache is indexed by two different keys: a record id number and a key created
* by combining the URI with a "secondary key", e.g. HTTP post data.
*
* @See nsINetDataCacheRecord
* @See nsINetDataCache
* @See nsINetDataDiskCache
* @See nsINetDataCacheManager
*/
[scriptable, uuid(6aeb2a40-6d43-11d3-90c8-000064657374)]
interface nsICachedNetData : nsISupports
{
/**
* String form of the URI provided as an argument to the call to
* nsINetDataCacheManager::GetCachedNetData() that created this record.
*/
readonly attribute string uriSpec;
/**
* Getter for the opaque secondary database key provided as an argument to
* the call to nsINetDataCacheManager::GetCachedNetData() that created this
* record.
*/
void getSecondaryKey(out unsigned long length,
[retval, size_is(length)] out string secondaryKey);
/**
* This flag may be set by a protocol handler to indicate that it supports
* partial fetching of data. In that case, the cache manager is permitted
* to truncate the entry's content to accommodate incoming data for other
* cache entries rather than deleting it wholesale.
*/
attribute boolean allowPartial;
/**
* This flag indicates that the write stream supplying content data for the
* cache did not complete normally and, therefore, the content may be
* truncated.
*/
readonly attribute boolean partialFlag;
/**
* This flag can be set and cleared by a protocol handler as a form of
* self-notification, so as to avoid race conditions in which a protocol
* handler issues two identical network requests to fill the same cache
* entry. The cache manager itself largely ignores this flag.
*/
attribute boolean updateInProgress;
/**
* inUse is set if any existing channels are associated with this cache
* entry or if the updateInProgess flag is set. This can be used to
* prevent writing to a cache entry by a protocol handler if it's being
* read or written elsewhere.
*/
readonly attribute boolean inUse;
/**
* Date/time that the document was last stored on the origin server, as
* supplied by the protocol handler. This value is used as input to the
* cache replacement policy, i.e. it is not used for validation. If the
* protocol can't supply a last-modified time, this attribute should remain
* unset. When unset, the value of this attribute is zero.
*
* FIXME: Should use nsIDateTime interface, once it's created
* instead of PRTime, for improved scriptability ?
*/
attribute PRTime lastModifiedTime;
/**
* Supplied by the protocol handler, the expirationTime attribute specifies
* the time until which the document is guaranteed fresh, i.e. the document
* does not have to be validated with the server and, therefore, any data
* in cache is definitely usable. The value of this attribute serves as a
* hint to the cache replacement policy. Only one of either staleTime or
* expirationTime may be set for a single cache record. When unset, the
* value of this attribute is zero.
*/
attribute PRTime expirationTime;
/**
* Date/time supplied by the protocol handler, at which point the content
* is *likely* to be stale, i.e. the data in the cache may be out-of-date
* with respect to the data on the server. This heuristic date does not
* necessarily correspond to the HTTP Expires header, as it does not
* determine when cached network data must be validated with the origin
* server, but only serves as a hint to the cache replacement policy. Only
* one of either staleTime or expirationTime may be set for a single cache
* record. When unset, the value of this attribute is zero.
*/
attribute PRTime staleTime;
/**
* Date/time of last access of the data in this cache record, as determined
* by the cache manager.
*/
readonly attribute PRTime lastAccessTime;
/**
* Number of times this record has been accessed since it was first stored.
*/
readonly attribute PRUint16 numberAccesses;
/**
* Accessor methods for opaque meta-data which can be read and updated
* independently of the content data.
*
* The aTag argument can be used to accommodate multiple clients of the
* cache API, each of which wants to store its own private meta-data into
* the cache. For example, there could be a "headers" tag that the HTTP
* protocol handler uses to store http response headers and a "image size"
* tag used to store the image dimensions of a GIF file. The aData
* argument refers to an opaque blob of arbitrary bytes.
*
* IMPORTANT: If aData does not contain byte-oriented data, i.e. it's not a
* string, the contents of aData must be byte-swapped by the,
* caller, so as to make the cache files endian-independent.
*/
void getAnnotation(in string aTag,
out PRUint32 aLength, [size_is(aLength), retval] out string aData);
void setAnnotation(in string aTag,
in PRUint32 aLength, [size_is(aLength)] in string aData);
/**
* As a getter, return the number of content bytes stored in the cache,
* i.e. via the nsIChannel streaming APIs. This may be less than the
* complete content length if a partial cache fill occurred. The cached
* content can be truncated by setting the value of this attribute. The
* value of the attribute represents a logical, not a physical, length. If
* compression has been used, the content may consume less storage than
* indicated by this attribute.
*
* When this attribute is set to zero the associated cache disk file, if
* any, should be deleted.
*/
attribute PRUint32 storedContentLength;
/**
* Notify any observers associated with this cache entry of the deletion
* request. If all observers drop their reference to the cache entry,
* proceed to delete the underlying cache database record and associated
* content storage.
*/
void delete();
/**
* Flush any changes in this entry's data to the cache database. This
* method will automatically be called when the last reference to the cache
* is dropped, but it can also be called explicitly for a synchronous
* effect.
*/
void commit();
/**
* Parent container cache for this entry.
*/
readonly attribute nsINetDataCache cache;
/**
* Create a channel for reading or writing a stream of content into the
* entry. It is expected that many of the nsIChannel methods return
* NS_NOT_IMPLEMENTED, including:
*
* + GetURI()
* + GetContentType()
* + GetContentLength()
*
* Though nsIChannel provides for both async and synchronous I/O APIs, both
* may not be implemented. Only AsyncRead() and OpenOutputStream() is
* required. The aProxyChannel argument allows another channel to be
* specified as the proffered argument to nsIStreamListener methods rather
* than the cache's own channel.
*/
nsIChannel newChannel(in nsILoadGroup aLoadGroup,
in nsIChannel aProxyChannel);
/**
* This method can be used by a caching protocol handler to store data in
* the cache by forking an asynchronous read stream so that it is
* simultaneously sent to a requester and written into the cache. This
* method implicitly sets the updateInProgress flag, if it has not already
* been set.
*/
nsIStreamListener interceptAsyncRead(in nsIStreamListener aOriginalListener,
in PRUint32 aStartOffset);
};

View File

@@ -0,0 +1,143 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsISupports.idl"
interface nsIURI;
interface nsINetDataCacheRecord;
interface nsISimpleEnumerator;
interface nsIFileSpec;
/**
* The nsINetDataCache defines the low-level API for a network-data
* cache, used to cache the responses to network retrieval commands.
* This interface, along with nsINetDataCacheRecord, is implemented by
* the memory cache, the file cache and, optionally, by some extension
* caches. This interface is essentially a pseudo-private API for the
* cache manager. Other clients should never use this interface.
*
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by two
* different keys: a record id number and a key created by combining the URI
* with a "secondary key", e.g. HTTP post data.
*
* The nsINetDataCache interface is agnostic as to where the data is
* stored and whether the storage is volatile or persistent. The
* memory cache, any disk caches and any extension caches must all
* implement this interface.
*
*/
[scriptable, uuid(ccfc58c0-6dde-11d3-90c8-000064657374)]
interface nsINetDataCache : nsISupports
{
/**
* Human-readable description of the cache module, e.g. "Disk Cache"
*/
readonly attribute wstring description;
/**
* Returns true if cached data is available for the given opaque key,
* even if only partial data is stored.
*/
boolean contains([size_is(length)] in string key, in PRUint32 length);
/**
* Fetch the cache entry record for the given opaque key. If one does not
* exist, create a new, empty record.
*/
nsINetDataCacheRecord getCachedNetData([size_is(length)] in string key,
in PRUint32 length);
/**
* Fetch the cache entry record for the given URI using the record ID as a key.
*/
nsINetDataCacheRecord getCachedNetDataByID(in PRInt32 RecordID);
/**
* False indicates that this cache is entirely bypassed.
*/
attribute boolean enabled;
/**
* Constants for flags attribute, below
*/
// Used for extension caches, e.g. a CD-ROM cache
const long READ_ONLY = 1 << 0;
// One of these bits must be set
const long MEMORY_CACHE = 1 << 1;
const long FLAT_FILE_CACHE = 1 << 2;
const long FILE_PER_URL_CACHE = 1 << 3;
/**
* See constants defined above.
*/
readonly attribute PRUint32 flags;
/**
* Total number of URI entries stored in the cache.
*/
readonly attribute PRUint32 numEntries;
/**
* Maximum number of URI entries that may be stored in the cache.
*/
readonly attribute PRUint32 maxEntries;
/**
* Enumerate the URI entries stored in the cache.
*/
nsISimpleEnumerator newCacheEntryIterator();
/**
* Contains a reference to the next cache in search order. For the memory
* cache, this attribute always references the disk cache. For the disk
* cache, it contains a reference to the first extension cache.
*/
attribute nsINetDataCache nextCache;
/**
* An estimate of the amount of storage occupied by the cache, in kB.
* Actual use may be slightly higher than reported due to cache overhead
* and heap fragmentation (in the memory cache) or block quantization (in
* the disk cache).
*/
readonly attribute PRUint32 storageInUse;
/**
* Remove all entries from a writable cache. This could be used, for
* example, after a guest ends a browser session. This is equivalent to
* setting the cache's Capacity to zero, except that all cache entries,
* even those in active use, will be deleted. Also, any global cache
* database files will be deleted.
*/
void removeAll();
};
%{ C++
// ProgID prefix for Components that implement this interface
#define NS_NETWORK_CACHE_PROGID "component://netscape/network/cache"
#define NS_NETWORK_MEMORY_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=memory-cache"
#define NS_NETWORK_FLAT_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=flat-cache"
#define NS_NETWORK_FILE_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=file-cache"
%}

View File

@@ -0,0 +1,163 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsISupports.idl"
interface nsISimpleEnumerator;
interface nsICachedNetData;
interface nsINetDataCache;
interface nsINetDataDiskCache;
interface nsIURI;
interface nsIFileSpec;
/**
* The network-response cache manager is partly responsible for the caching of
* content and associated metadata that has been retrieved via the network.
* (The remaining responsibility for caching lies with individual network
* protocol handlers.)
*
* The cache manager supervises the actions of individual cache components,
* such as the memory cache, the disk cache and any extension caches, e.g. a
* read-only CD-ROM cache.
*
* @See nsINetDataCache
* @See nsICachedNetData
*/
[scriptable, uuid(71c8ab00-6d5c-11d3-90c8-000064657374)]
interface nsINetDataCacheManager : nsISupports
{
/**
* Flag for the GetCachedNetData() method: If set, the memory cache is
* neither searched nor will any data be stored into it. This might be
* appropriate, for example, with images, because they have their own
* cache for storing *decoded* images.
*/
const unsigned long BYPASS_MEMORY_CACHE = 1 << 0;
/**
* Flag for the GetCachedNetData() method: If set, the disk cache
* is neither searched nor will any be data stored into it.
* However, read-only extension caches may be searched. This
* might be used to avoid leaving persistent records of secure
* data.
*/
const unsigned long BYPASS_PERSISTENT_CACHE = 1 << 1;
/**
* Flag for the GetCachedNetData() method: If set, any stream
* content is stored in the cache as a single disk file. Content
* will not be cached in the memory cache nor is it cached in a
* flat-file cache database. This is used to implement the jar
* protocol handler and to provide the stream-as-file semantics
* required by the classic bowser plugin API.
*/
const unsigned long CACHE_AS_FILE = 1 << 2;
/**
* Fetch the cache entry record for the given URI. If one does not exist,
* create a new, empty record. The normal search order for caches is:
* + Memory cache
* + Disk cache
* + File cache (stream-as-file cache)
* + All extension caches
*
* When writing, data is typically stored in both the memory cache and the
* disk cache. Both the search order and this write policy can be modified by
* setting one or more of the flag argument bits, as defined above.
*
* The optionally-NULL secondaryKey argument can be used, e.g. for form
* post data or for HTTP headers in the case of HTTP.
*/
nsICachedNetData getCachedNetData(in string uri,
[size_is(secondaryKeyLength)] in string secondaryKey,
in PRUint32 secondaryKeyLength,
in PRUint32 flags);
/**
* Returns true if cached content is available for the given URI, even if
* only partial data is stored. The flags argument behaves the same as for
* the GetCachedNetData() method, above.
*/
boolean contains(in string uri,
[size_is(secondaryKeyLength)] in string secondaryKey,
in PRUint32 secondaryKeyLength,
in PRUint32 flags);
/**
* Total number of unexpired URI entries stored in all caches. This number
* does not take into account duplicate URIs, e.g. because the memory cache
* and the disk cache might each contain an entry for the same URI.
*/
readonly attribute PRUint32 numEntries;
/**
* Enumerate the unexpired URI entries stored in all caches. Some URIs may
* be enumerated more than once, e.g. because the the memory cache and the
* disk cache might each contain an entry for the same URI.
*/
nsISimpleEnumerator newCacheEntryIterator();
/*
* Enumerate all the loaded nsINetDataCache-implementing cache modules.
* The first module enumerated will be the memory cache, the second will be
* the disk cache, then the file cache, followed by all the extension
* caches, in search order.
*/
nsISimpleEnumerator newCacheModuleIterator();
/**
* Remove all entries from all writable caches. This could be used, for
* example, after a guest ends a browser session. This is equivalent to
* setting the DiskCacheCapacity to zero, except that all cache entries,
* even those in active use, will be deleted. Also, any global cache
* database files will be deleted.
*/
void RemoveAll();
/**
* The disk cache is made up of the file cache (for stream-as-file
* requests) and a (possibly independent) persistent cache that handles all
* other cache requests. This attribute sets/gets the combined capacity of
* these caches, measured in KBytes. Setting the capacity lower than the
* current amount of space currently in use may cause cache entries to be
* evicted from the cache to accomodate the requested capacity.
*/
attribute PRUint32 diskCacheCapacity;
/**
* This attribute sets/gets the capacity of the memory cache, measured in
* KBytes. Setting the capacity lower than the current amount of space
* currently in use may cause cache entries to be evicted from the cache to
* accomodate the requested capacity.
*/
attribute PRUint32 memCacheCapacity;
/**
* This attribute must be set before attempting to store into the disk cache.
*/
attribute nsIFileSpec diskCacheFolder;
};
%{ C++
// ProgID prefix for Components that implement this interface
#define NS_NETWORK_CACHE_MANAGER_PROGID NS_NETWORK_CACHE_PROGID "?name=manager"
%}

View File

@@ -0,0 +1,125 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsISupports.idl"
#include "nsrootidl.idl"
interface nsIFileSpec;
interface nsIChannel;
interface nsINetDataCache;
/**
* The nsINetDataCacheRecord represents a single entry in a database that
* caches data retrieved from the network. On top of this low-level interface
* to the raw record data, the cache manager implements a higher-level record
* interface, nsICachedNetData. Each instance of nsINetDataCacheRecord is
* (internally) associated with a parent database, an instance of the
* nsINetDataCache interface. This interface is essentially a pseudo-private
* API for the cache manager. Other clients should never use this interface.
*
* Each cache record may contain both content and metadata. The content may
* be, for example, GIF image data or HTML, and it is accessed through
* nsIChannel's streaming API. The opaque metadata, which may contain HTTP
* headers among other things, is accessed as a contiguous byte array. Each
* entry in the cache is indexed by two different keys: a unique record id
* number, generated by the cache, and an opaque string. The latter contains
* the URI and other secondary key information, e.g. HTTP form post key/value
* pairs.
*
* The nsINetDataCacheRecord interface is agnostic as to where the data is
* stored and whether the storage is volatile or persistent. The memory cache,
* the disk cache, a flat-file cache and any read-only extension caches must
* all implement this interface.
*
* @See nsICachedNetData
* @See nsINetDataCache
* @See nsINetDataDiskCache
* @See nsINetDataCacheManager
*/
interface nsILoadGroup;
[scriptable, uuid(fdcdd6a0-7461-11d3-90ca-0040056a906e)]
interface nsINetDataCacheRecord : nsISupports
{
/**
* As far as the nsINetDataCacheRecord implementation is concerned, the
* cache entry database key is an opaque blob, but it's intended to contain
* both the URI and any secondary keys, such as HTTP post data.
*/
void getKey(out unsigned long length, [size_is(length), retval] out string key);
/**
* A persistent record number assigned by the cache which must be unique
* among all entries stored within the same cache. The record ID serves as
* an alternate key to the cache record. Providing that they satisfy the
* afforementioned uniqueness requirement, record IDs can be assigned any
* value by the database except that they may never be zero.
*/
readonly attribute PRInt32 recordID;
/**
* Opaque data which can be updated for each cache entry independently of
* the content data. This data is a combination of protocol-independent
* data provided by the cache manager and protocol-specific meta-data,
* e.g. HTTP headers.
*/
void getMetaData(out PRUint32 length, [size_is(length), retval] out string metaData);
void setMetaData(in PRUint32 length, [size_is(length)] in string data);
/**
* Number of content bytes stored in the cache, i.e. via the nsIChannel
* streaming APIs. This may be less than the complete content length if a
* partial cache fill occurred. Additionally, the cached content can be
* truncated by reducing the value of this attribute. When this attribute
* is set to zero the associated cache disk file, if any, should be
* deleted.
*/
attribute PRUint32 storedContentLength;
/**
* Delete this cache entry and its associated content.
*/
void delete();
/**
* Create a channel for reading or writing a stream of content into the
* entry. However, many of the nsIChannel methods may return
* NS_NOT_IMPLEMENTED, including:
*
* + GetURI()
* + GetContentType()
* + GetContentLength()
*/
nsIChannel newChannel(in nsILoadGroup loadGroup);
/**
* If a cache is implemented such that it stores each URI's content in an
* individual disk file, this method will identify the file corresponding
* to this record. This may be used to implement the "stream-as-file"
* semantics required by some plugins and by the 'jar:' protocol handler.
* However, not all cache implementations are *required* to store the data
* from each URI in an individual file, so it is acceptable for an
* implementation of this method to signal NS_NOT_IMPLEMENTED.
*/
readonly attribute nsIFileSpec filename;
};

View File

@@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsINetDataCache.idl"
interface nsIFileSpec;
/**
/**
* A network-data disk cache is used to persistently cache the responses to
* network retrieval commands. Each cache entry may contain both content,
* e.g. GIF image data, and associated metadata, e.g. HTTP headers.
*/
[scriptable, uuid(6408e390-6f13-11d3-90c8-000064657374)]
interface nsINetDataDiskCache : nsINetDataCache
{
/**
* This attribute must be set before calling any other methods of this
* interface.
*/
attribute nsIFileSpec diskCacheFolder;
};

View File

@@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsrootidl.idl"
#include "nsISupports.idl"
interface nsIFileSpec;
interface nsIStreamAsFileObserver;
/**
* In addition to enhancing effective network response time via caching, the
* cache manager serves a second purpose by providing the stream-as-file
* service required by traditional browser plugins and the jar: protocol
* handler. The interface below provides a means for a client to determine the
* filename associated with a stream and to detect modification/deletion of
* that file.
*/
[scriptable, uuid(0eedbbf0-92d9-11d3-90d3-0040056a906e)]
interface nsIStreamAsFile : nsISupports
{
/**
* Filename containing stream-as-file
*/
readonly attribute nsIFileSpec fileSpec;
/**
* Add an observer for this cache record. When the cache wants to delete
* or truncate a record, so as to make space for another cache entry's
* content data, it will call <code>aObserver</code>'s Observe() method,
* passing the nsIStreamAsFile instance as the <code>aSubject</code>
* argument and an appropriate message. If the observer does not wish to
* inhibit deletion/truncation, it should Release() any references it has to the
* cache record.
*
* @See nsIStreamAsFileObserver
*/
void addObserver(in nsIStreamAsFileObserver aObserver);
/**
* Delete an observer that was added by the AddObserver() method.
*/
void removeObserver(in nsIStreamAsFileObserver aObserver);
};
/**
* This interface can be implemented by a client to receive notifications of
* either modification or deletion of a file created by the cache manager using
* the stream-as-file semantics.
*/
[scriptable, uuid(a26e27c0-92da-11d3-90d3-0040056a906e)]
interface nsIStreamAsFileObserver : nsISupports
{
/**
* Flag bits for argument to Observe() method.
*/
const long NOTIFY_AVAILABLE = 1 << 0; // Stream-as-file now available for reading
const long NOTIFY_ERROR = 1 << 1; // Error while loading stream / creating file
const long REQUEST_DELETION = 1 << 2; // Cache manager wishes to delete/truncate file
const long INVALIDATE = 1 << 3; // File is out-of-date
// Convenience value
const long MAKE_UNAVAILABLE = REQUEST_DELETION | INVALIDATE;
/**
* Receive either a notification or a request concerning a file that has
* been opened using stream-as-file. The aMessage and aError arguments
* have varying values depending on the nature of the notification.
* aMessage is set to NOTIFY_AVAILABLE when a complete stream has been read
* and stored on disk in a file. At that point, and no sooner, may the
* filename attribute of the associated nsIStreamAsFile be accessed via the
* associated nsIStreamAsFile interface. If the aMessage argument is
* NOTIFY_ERROR, the aError argument contains the relevant error code. If
* the aMessage argument is either REQUEST_DELETION or REQUEST_TRUNCATION,
* the callee should immediately Release() all references to the
* nsIStreamAsFile (and any references to its associated nsICachedNetData
* instances), unless it wishes to inhibit the requested file modification.
* If the aMessage argument is INVALIDATE, the cache manager is replacing
* the file with a more recent version. If a client wants to continue
* using the (now out-of-date) file, it must delete it when it has finished,
* as the cache manager will effectively relinquished ownership of the
* file.
*/
void ObserveStreamAsFile(in nsIStreamAsFile aStreamAsFile,
in PRUint32 aMessage,
in nsresult aError);
};

View File

@@ -1,5 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
</IfModule>

View File

@@ -1,69 +0,0 @@
<?php
/* SVN FILE: $Id: app_controller.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* Short description for file.
*
* This file is application-wide controller file. You can put all
* application-wide controller-related methods here.
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.cake
* @since CakePHP v 0.2.9
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Short description for class.
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* @package cake
* @subpackage cake.cake
*/
uses('Sanitize');
class AppController extends Controller {
/**
* This function is intended to be used with url parameters when passing them to
* a view. (This is useful when echoing values out in <input> tags, etc.
* Note that the keys to the arrays are escaped as well.
*
* @param array dirty parameters
* @return array cleaned values
*/
function decodeAndSanitize($params)
{
$clean = array();
foreach ($params as $var => $val) {
$var = $this->Sanitize->html(urldecode($var));
$val = $this->Sanitize->html(urldecode($val));
$clean[$var] = $val;
}
return $clean;
}
}
?>

View File

@@ -1,72 +0,0 @@
<?php
/* SVN FILE: $Id: app_model.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* Application model for Cake.
*
* This file is application-wide model file. You can put all
* application-wide model-related methods here.
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.cake
* @since CakePHP v 0.2.9
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Application model for Cake.
*
* Add your application-wide methods in the class below, your models
* will inherit them.
*
* @package cake
* @subpackage cake.cake
*/
uses('sanitize');
class AppModel extends Model {
/**
* Will clean arrays for input into SQL.
* Note that the array keys are getting cleaned here as well. If you're using strings
* (with escapable characters in them) as keys to your array, be extra careful.
*
* @access public
* @param array to be cleaned
* @return array with sql escaped
*/
function cleanArrayForSql($array)
{
$sanitize = new Sanitize();
$clean = array();
foreach ($array as $var => $val)
{
$var = $sanitize->sql($var);
$val = $sanitize->sql($val);
$clean[$var] = $val;
}
return $clean;
}
}
?>

View File

@@ -1,76 +0,0 @@
;<?php die() ?>
; SVN FILE: $Id: acl.ini.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $
;/**
; * Short description for file.
; *
; *
; * PHP versions 4 and 5
; *
; * CakePHP : Rapid Development Framework <http://www.cakephp.org/>
; * Copyright (c) 2006, Cake Software Foundation, Inc.
; * 1785 E. Sahara Avenue, Suite 490-204
; * Las Vegas, Nevada 89104
; *
; * Licensed under The MIT License
; * Redistributions of files must retain the above copyright notice.
; *
; * @filesource
; * @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
; * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
; * @package cake
; * @subpackage cake.app.config
; * @since CakePHP v 0.10.0.1076
; * @version $Revision: 1.1.1.1 $
; * @modifiedby $LastChangedBy: phpnut $
; * @lastmodified $Date: 2006-05-24 19:14:24 $
; * @license http://www.opensource.org/licenses/mit-license.php The MIT License
; */
; acl.ini.php - Cake ACL Configuration
; ---------------------------------------------------------------------
; Use this file to specify user permissions.
; aco = access control object (something in your application)
; aro = access request object (something requesting access)
;
; User records are added as follows:
;
; [uid]
; groups = group1, group2, group3
; allow = aco1, aco2, aco3
; deny = aco4, aco5, aco6
;
; Group records are added in a similar manner:
;
; [gid]
; allow = aco1, aco2, aco3
; deny = aco4, aco5, aco6
;
; The allow, deny, and groups sections are all optional.
; NOTE: groups names *cannot* ever be the same as usernames!
;
; ACL permissions are checked in the following order:
; 1. Check for user denies (and DENY if specified)
; 2. Check for user allows (and ALLOW if specified)
; 3. Gather user's groups
; 4. Check group denies (and DENY if specified)
; 5. Check group allows (and ALLOW if specified)
; 6. If no aro, aco, or group information is found, DENY
;
; ---------------------------------------------------------------------
;-------------------------------------
;Users
;-------------------------------------
[username-goes-here]
groups = group1, group2
deny = aco1, aco2
allow = aco3, aco4
;-------------------------------------
;Groups
;-------------------------------------
[groupname-goes-here]
deny = aco5, aco6
allow = aco7, aco8

View File

@@ -1,50 +0,0 @@
<?php
/* SVN FILE: $Id: bootstrap.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* Short description for file.
*
* Long description for file
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.app.config
* @since CakePHP v 0.10.8.2117
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
*
* This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php is loaded
* This is an application wide file to load any function that is not used within a class define.
* You can also use this to include or require any files in your application.
*
*/
/**
* The settings below can be used to set additional paths to models, views and controllers.
* This is related to Ticket #470 (https://trac.cakephp.org/ticket/470)
*
* $modelPaths = array('full path to models', 'second full path to models', 'etc...');
* $viewPaths = array('this path to views', 'second full path to views', 'etc...');
* $controllerPaths = array('this path to controllers', 'second full path to controllers', 'etc...');
*
*/
//EOF
?>

View File

@@ -1,153 +0,0 @@
<?php
/* SVN FILE: $Id: core.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* This is core configuration file.
*
* Use it to configure core behaviour ofCake.
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.app.config
* @since CakePHP v 0.2.9
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* If you do not have mod rewrite on your system
* or if you prefer to use CakePHP pretty urls.
* uncomment the line below.
* Note: If you do have mod rewrite but prefer the
* CakePHP pretty urls, you also have to remove the
* .htaccess files
* release/.htaccess
* release/app/.htaccess
* release/app/webroot/.htaccess
*/
//define ('BASE_URL', env('SCRIPT_NAME'));
/**
* Set debug level here:
* - 0: production
* - 1: development
* - 2: full debug with sql
* - 3: full debug with sql and dump of the current object
*
* In production, the "flash messages" redirect after a time interval.
* With the other debug levels you get to click the "flash message" to continue.
*
*/
define('DEBUG', 0);
/**
* Turn of caching checking wide.
* You must still use the controller var cacheAction inside you controller class.
* You can either set it controller wide, or in each controller method.
* use var $cacheAction = true; or in the controller method $this->cacheAction = true;
*/
define ('CACHE_CHECK', false);
/**
* Error constant. Used for differentiating error logging and debugging.
* Currently PHP supports LOG_DEBUG
*/
define ('LOG_ERROR', 2);
/**
* CakePHP includes 3 types of session saves
* database or file. Set this to your preferred method.
* If you want to use your own save handler place it in
* app/config/name.php DO NOT USE file or database as the name.
* and use just the name portion below.
*
* Setting this to cake will save files to /cakedistro/tmp directory
* Setting it to php will use the php default save path
* Setting it to database will use the database
*
*
*/
define('CAKE_SESSION_SAVE', 'php');
/**
* Set a random string of used in session.
*
*/
define('CAKE_SESSION_STRING', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi');
/**
* Set the name of session cookie
*
*/
define('CAKE_SESSION_COOKIE', 'CAKEPHP');
/**
* Set level of Cake security.
*
*/
define('CAKE_SECURITY', 'high');
/**
* Set Cake Session time out.
* If CAKE_SECURITY define is set
* high: multiplied by 10
* medium: is multiplied by 100
* low is: multiplied by 300
*
* Number below is seconds.
*/
define('CAKE_SESSION_TIMEOUT', '120');
/**
* Uncomment the define below to use cake built in admin routes.
* You can set this value to anything you want.
* All methods related to the admin route should be prefixed with the
* name you set CAKE_ADMIN to.
* For example: admin_index, admin_edit
*/
//define('CAKE_ADMIN', 'admin');
/**
* The define below is used to turn cake built webservices
* on or off. Default setting is off.
*/
define('WEBSERVICES', 'off');
/**
* Compress output CSS (removing comments, whitespace, repeating tags etc.)
* This requires a/var/cache directory to be writable by the web server (caching).
* To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use Controller::cssTag().
*/
define('COMPRESS_CSS', false);
/**
* If set to true, helpers would output data instead of returning it.
*/
define('AUTO_OUTPUT', false);
/**
* If set to false, session would not automatically be started.
*/
define('AUTO_SESSION', true);
/**
* Set the max size of file to use md5() .
*/
define('MAX_MD5SIZE', (5*1024)*1024 );
/**
* To use Access Control Lists with Cake...
*/
define('ACL_CLASSNAME', 'DB_ACL');
define('ACL_FILENAME', 'dbacl'.DS.'db_acl');
?>

View File

@@ -1,79 +0,0 @@
<?php
/* SVN FILE: $Id: database.php.default,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* This is core configuration file.
*
* Use it to configure core behaviour ofCake.
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.app.config
* @since CakePHP v 0.2.9
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* In this file you set up your database connection details.
*
* @package cake
* @subpackage cake.config
*/
/**
* Database configuration class.
* You can specify multiple configurations for production, development and testing.
*
* driver =>
* mysql, postgres, sqlite, adodb-drivername, pear-drivername
*
* connect =>
* MySQL set the connect to either mysql_pconnect of mysql_connect
* PostgreSQL set the connect to either pg_pconnect of pg_connect
* SQLite set the connect to sqlite_popen sqlite_open
* ADOdb set the connect to one of these
* (http://phplens.com/adodb/supported.databases.html) and
* append it '|p' for persistent connection. (mssql|p for example, or just mssql for not persistent)
*
* host =>
* the host you connect to the database
* MySQL 'localhost' to add a port number use 'localhost:port#'
* PostgreSQL 'localhost' to add a port number use 'localhost port=5432'
*
*/
class DATABASE_CONFIG
{
var $default = array('driver' => 'mysql',
'connect' => 'mysql_connect',
'host' => 'localhost',
'login' => 'user',
'password' => 'password',
'database' => 'project_name',
'prefix' => '');
var $test = array('driver' => 'mysql',
'connect' => 'mysql_connect',
'host' => 'localhost',
'login' => 'user',
'password' => 'password',
'database' => 'project_name-test',
'prefix' => '');
}
?>

View File

@@ -1,74 +0,0 @@
<?php
/* SVN FILE: $Id: inflections.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* Custom Inflected Words.
*
* This file is used to hold words that are not matched in the normail Inflector::pluralize() and
* Inflector::singularize()
*
* PHP versions 4 and %
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.app.config
* @since CakePHP v 1.0.0.2312
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* This is a key => value array of regex used to match words.
* If key matches then the value is returned.
*
* $pluralRules = array('/(s)tatus$/i' => '\1\2tatuses', '/^(ox)$/i' => '\1\2en', '/([m|l])ouse$/i' => '\1ice');
*/
$pluralRules = array();
/**
* This is a key only array of plural words that should not be inflected.
* Notice the last comma
*
* $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox');
*/
$uninflectedPlural = array();
/**
* This is a key => value array of plural irregular words.
* If key matches then the value is returned.
*
* $irregularPlural = array('atlas' => 'atlases', 'beef' => 'beefs', 'brother' => 'brothers')
*/
$irregularPlural = array();
/**
* This is a key => value array of regex used to match words.
* If key matches then the value is returned.
*
* $singularRules = array('/(s)tatuses$/i' => '\1\2tatus', '/(matr)ices$/i' =>'\1ix','/(vert|ind)ices$/i')
*/
$singularRules = array();
/**
* This is a key only array of singular words that should not be inflected.
* You should not have to change this value below if you do change it use same format
* as the $uninflectedPlural above.
*/
$uninflectedSingular = $uninflectedPlural;
/**
* This is a key => value array of singular irregular words.
* Most of the time this will be a reverse of the above $irregularPlural array
* You should not have to change this value below if you do change it use same format
*
* $irregularSingular = array('atlases' => 'atlas', 'beefs' => 'beef', 'brothers' => 'brother')
*/
$irregularSingular = array_flip($irregularPlural);
?>

View File

@@ -1,51 +0,0 @@
<?php
/* SVN FILE: $Id: routes.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* Short description for file.
*
* In this file, you set up routes to your controllers and their actions.
* Routes are very important mechanism that allows you to freely connect
* different urls to chosen controllers and their actions (functions).
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.app.config
* @since CakePHP v 0.2.9
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Here, we are connecting '/' (base path) to controller called 'Pages',
* its action called 'display', and we pass a param to select the view file
* to use (in this case, /app/views/pages/home.thtml)...
*/
$Route->connect ('/', array('controller'=>'results', 'action'=>'', ''));
/**
* ...and connect the rest of 'Pages' controller's urls.
*/
$Route->connect ('/pages/*', array('controller'=>'pages', 'action'=>'display'));
/**
* Then we connect url '/test' to our test controller. This is helpfull in
* developement.
*/
$Route->connect ('/tests', array('controller'=>'tests', 'action'=>'index'));
?>

View File

@@ -1,30 +0,0 @@
CREATE TABLE `acos` (
`id` int(11) NOT NULL auto_increment,
`model` varchar(255) NOT NULL default '',
`object_id` int(11) default NULL,
`alias` varchar(255) NOT NULL default '',
`lft` int(11) default NULL,
`rght` int(11) default NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `aros` (
`id` int(11) NOT NULL auto_increment,
`model` varchar(255) NOT NULL default '',
`user_id` int(11) default NULL,
`alias` varchar(255) NOT NULL default '',
`lft` int(11) default NULL,
`rght` int(11) default NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `aros_acos` (
`id` int(11) NOT NULL auto_increment,
`aro_id` int(11) default NULL,
`aco_id` int(11) default NULL,
`_create` int(1) NOT NULL default '0',
`_read` int(1) NOT NULL default '0',
`_update` int(1) NOT NULL default '0',
`_delete` int(11) NOT NULL default '0',
PRIMARY KEY (`id`)
);

View File

@@ -1,11 +0,0 @@
-- @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
-- @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
-- @since CakePHP v 0.10.8.1997
-- @version $Revision: 1.1.1.1 $
CREATE TABLE cake_sessions (
id varchar(255) NOT NULL default '',
data text,
expires int(11) default NULL,
PRIMARY KEY (id)
);

View File

@@ -1,7 +0,0 @@
<?php
class ApplicationsController extends AppController {
var $name = 'Applications';
}
?>

View File

@@ -1,7 +0,0 @@
<?php
class IntentionsController extends AppController {
var $name = 'Intentions';
}
?>

View File

@@ -1,7 +0,0 @@
<?php
class IssuesController extends AppController {
var $name = 'Issues';
}
?>

View File

@@ -1,122 +0,0 @@
<?php
/* Include the CSV library. It would be nice to make this OO sometime */
vendor('csv/csv');
class ResultsController extends AppController {
var $name = 'Results';
/**
* Model's this controller uses
* @var array
*/
var $uses = array('Application','Result');
/**
* Cake Helpers
* @var array
*/
var $helpers = array('Html', 'Javascript', 'Export', 'Pagination','Time');
/**
* Pagination helper variable array
* @access public
* @var array
*/
var $pagination_parameters = array();
/**
* Will hold a sanitize object
* @var object
*/
var $Sanitize;
/**
* Constructor - sets up the sanitizer and the pagination
*/
function ResultsController()
{
parent::AppController();
$this->Sanitize = new Sanitize();
// Pagination Stuff
$this->pagination_parameters['show'] = empty($_GET['show'])? '10' : $this->Sanitize->paranoid($_GET['show']);
$this->pagination_parameters['sortBy'] = empty($_GET['sort'])? 'created' : $this->Sanitize->paranoid($_GET['sort']);
$this->pagination_parameters['direction'] = empty($_GET['direction'])? 'desc': $this->Sanitize->paranoid($_GET['direction']);
$this->pagination_parameters['page'] = empty($_GET['page'])? '1': $this->Sanitize->paranoid($_GET['page']);
$this->pagination_parameters['order'] = $this->modelClass.'.'.$this->pagination_parameters['sortBy'].' '.strtoupper($this->pagination_parameters['direction']);
}
/**
* Front page will show the graph
*/
function index()
{
// Products dropdown
$this->set('products', $this->Application->getApplications());
// Fill in all the data passed in $_GET
$this->set('url_params',$this->decodeAndSanitize($this->params['url']));
// We'll need to include the graphing libraries
$this->set('include_graph_libraries', true);
// Core data to show on page
$this->set('descriptionAndTotalsData',$this->Result->getDescriptionAndTotalsData($this->params['url']));
}
/**
* Display a table of user comments
*/
function comments()
{
// Products dropdown
$this->set('products', $this->Application->getApplications());
// Fill in all the data passed in $_GET
$this->set('url_params',$this->decodeAndSanitize($this->params['url']));
// Pagination settings
$paging['style'] = 'html';
$paging['link'] = "/results/comments/?product=".urlencode($this->params['url']['product'])."&start_date=".urlencode($this->params['url']['start_date'])."&end_date=".urlencode($this->params['url']['end_date'])."&show={$this->pagination_parameters['show']}&sort={$this->pagination_parameters['sortBy']}&direction={$this->pagination_parameters['direction']}&page=";
$paging['count'] = $this->Result->getCommentCount($this->params['url']);
$paging['page'] = $this->pagination_parameters['page'];
$paging['limit'] = $this->pagination_parameters['show'];
$paging['show'] = array('10','25','50');
// No point in showing them an error if they click on "show 50" but they are
// already on the last page.
if ($paging['count'] < ($this->pagination_parameters['page'] * ($this->pagination_parameters['show']/2))) {
$this->pagination_parameters['page'] = $paging['page'] = 1;
}
// Set pagination array
$this->set('paging',$paging);
// Core data to show on page
$this->set('commentsData',$this->Result->getComments($this->params['url'], $this->pagination_parameters));
}
/**
* Display a csv
*/
function csv()
{
// Get rid of the header/footer/etc.
$this->layout = null;
// Our CSV library sends headers and everything. Keep the view empty!
csv_send_csv($this->Result->getCsvExportData($this->params['url']));
// I'm not exiting here in case someone is going to use post callback stuff.
// In development, that means extra lines get added to our CSVs, but in
// production it should be clean.
}
}
?>

View File

@@ -1,27 +0,0 @@
<?php
/* SVN FILE: $Id: index.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.app
* @since CakePHP v 0.10.0.1076
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
require 'webroot'.DIRECTORY_SEPARATOR.'index.php';
?>

View File

@@ -1,24 +0,0 @@
<?php
class Application extends AppModel {
var $name = 'Application';
var $hasOne = array('Result');
var $hasAndBelongsToMany = array(
'Intention' => array('className' => 'Intention'),
'Issue' => array('className' => 'Issue')
);
/**
* This was added because running findAll() on this model does a left join on the
* results table which takes around 10 seconds to grab all the data. All I want
* is a list of the applications...
*
* @return array rows representing each application
*/
function getApplications()
{
return $this->query('SELECT * FROM `applications` ORDER BY `id`');
}
}
?>

View File

@@ -1,12 +0,0 @@
<?php
class Intention extends AppModel {
var $name = 'Intention';
var $hasOne = array('Result');
var $hasAndBelongsToMany = array('Application' =>
array('className' => 'Application')
);
}
?>

View File

@@ -1,10 +0,0 @@
<?php
class Issue extends AppModel {
var $name = 'Issue';
var $hasAndBelongsToMany = array(
'Application' => array('className' => 'Application'),
'Result' => array('className' => 'Result')
);
}
?>

View File

@@ -1,339 +0,0 @@
<?php
class Result extends AppModel {
var $name = 'Result';
var $belongsTo = array('Application', 'Intention');
var $hasAndBelongsToMany = array('Issue' =>
array('className' => 'Issue')
);
/**
* Count's all the comments, according to the parameters.
* @param array URL parameters
* @return Cake's findCount() value
*/
function getCommentCount($params)
{
// Clean parameters
$params = $this->cleanArrayForSql($params);
// We only want to see rows with comments
$_conditions = array("comments NOT LIKE ''");
if (!empty($params['start_date'])) {
$_timestamp = strtotime($params['start_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
array_push($_conditions, "`created` >= '{$_date}'");
}
}
if (!empty($params['end_date'])) {
$_timestamp = strtotime($params['end_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
array_push($_conditions, "`created` <= '{$_date}'");
}
}
if (!empty($params['product'])) {
// product's come in looking like:
// Mozilla Firefox 1.5.0.1
$_exp = explode(' ',urldecode($params['product']));
if(count($_exp) == 3) {
$_product = $_exp[0].' '.$_exp[1];
$_version = $_exp[2];
/* Note that 'Application' is not the actual name of the table! You can
* thank cake for that.*/
array_push($_conditions, "`Application`.`name` LIKE '%{$_product}%'");
array_push($_conditions, "`Application`.`version` LIKE '%{$_version}%'");
} else {
// defaults I guess?
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'");
}
} else {
// I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'");
}
// Do the actual query
$comments = $this->findCount($_conditions);
return $comments;
}
/**
* Will retrieve all the comments within param's and pagination's parameters
* @param array URL parameters
* @param array pagination values from the controller
* @param boolean if privacy is true phone numbers and email addresses will be
* masked
* @return cake result set
*/
function getComments($params, $pagination, $privacy=true)
{
$params = $this->cleanArrayForSql($params);
// We only want to see rows with comments
$_conditions = array("comments NOT LIKE ''");
if (!empty($params['start_date'])) {
$_timestamp = strtotime($params['start_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
array_push($_conditions, "`created` >= '{$_date}'");
}
}
if (!empty($params['end_date'])) {
$_timestamp = strtotime($params['end_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
array_push($_conditions, "`created` <= '{$_date}'");
}
}
if (!empty($params['product'])) {
// product's come in looking like:
// Mozilla Firefox 1.5.0.1
$_exp = explode(' ',urldecode($params['product']));
if(count($_exp) == 3) {
$_product = $_exp[0].' '.$_exp[1];
$_version = $_exp[2];
/* Note that 'Application' is not the actual name of the table! You can
* thank cake for that.*/
array_push($_conditions, "`Application`.`name` LIKE '%{$_product}%'");
array_push($_conditions, "`Application`.`version` LIKE '%{$_version}%'");
} else {
// defaults I guess?
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'");
}
} else {
// I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'");
}
$comments = $this->findAll($_conditions, null, $pagination['order'], $pagination['show'], $pagination['page']);
if ($privacy) {
// Pull out all the email addresses and phone numbers
foreach ($comments as $var => $val) {
// Handle foo@bar.com
$_email_regex = '/\ ?(.+)?@(.+)?\.(.+)?\ ?/';
$comments[$var]['Result']['comments'] = preg_replace($_email_regex,'$1@****.$3',$comments[$var]['Result']['comments']);
$comments[$var]['Result']['intention_text'] = preg_replace($_email_regex,'$1@****.$3',$comments[$var]['Result']['intention_text']);
// Handle xxx-xxx-xxxx
$_phone_regex = '/([0-9]{3})[ .-]?[0-9]{4}/';
$comments[$var]['Result']['comments'] = preg_replace($_phone_regex,'$1-****',$comments[$var]['Result']['comments']);
$comments[$var]['Result']['intention_text'] = preg_replace($_phone_regex,'$1-****',$comments[$var]['Result']['intention_text']);
}
}
return $comments;
}
/**
* This function runs the query to get the export data for the CSV file.
*
* @param array URL parameters
* @param boolean if privacy is true phone numbers and email addresses will be
* masked
* @return array two dimensional array that should be pretty easy to transform
* into a CSV.
*/
function getCsvExportData($params, $privacy=true)
{
$params = $this->cleanArrayForSql($params);
// We have to use a left join here because there isn't always an intention
$_query = "
SELECT
`results`.`id`,
`results`.`created`,
`results`.`intention_text` as `intention_other`,
`results`.`comments`,
`intentions`.`description` as `intention`
FROM `results`
LEFT JOIN `intentions` ON `results`.`intention_id`=`intentions`.`id`
INNER JOIN `applications` ON `applications`.`id` = `results`.`application_id`
WHERE
1=1
";
if (!empty($params['start_date'])) {
$_timestamp = strtotime($params['start_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
$_query .= " AND `results`.`created` >= '{$_date}'";
}
}
if (!empty($params['end_date'])) {
$_timestamp = strtotime($params['end_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
$_query .= " AND `results`.`created` <= '{$_date}'";
}
}
if (!empty($params['product'])) {
// product's come in looking like:
// Mozilla Firefox 1.5.0.1
$_exp = explode(' ',urldecode($params['product']));
if(count($_exp) == 3) {
$_product = $_exp[0].' '.$_exp[1];
$_version = $_exp[2];
$_query .= " AND `applications`.`name` LIKE '{$_product}'";
$_query .= " AND `applications`.`version` LIKE '{$_version}'";
} else {
// defaults I guess?
$_query .= " AND `applications`.`name` LIKE 'Mozilla Firefox'";
$_query .= " AND `applications`.`version` LIKE '1.5'";
}
} else {
// I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired
$_query .= " AND `applications`.`name` LIKE 'Mozilla Firefox'";
$_query .= " AND `applications`.`version` LIKE '1.5'";
}
$_query .= " ORDER BY `results`.`created` ASC";
$res = $this->query($_query);
// Since we're exporting to a CSV, we need to flatten the results into a 2
// dimensional table array
$newdata = array();
foreach ($res as $result) {
$newdata[] = array_merge($result['results'], $result['intentions']);
}
if ($privacy) {
// Pull out all the email addresses and phone numbers
foreach ($newdata as $var => $val) {
// Handle foo@bar.com
$_email_regex = '/\ ?(.+)?@(.+)?\.(.+)?\ ?/';
$newdata[$var]['comments'] = preg_replace($_email_regex,'$1@****.$3',$newdata[$var]['comments']);
$newdata[$var]['intention_other'] = preg_replace($_email_regex,'$1@****.$3',$newdata[$var]['intention_other']);
// Handle xxx-xxx-xxxx
$_phone_regex = '/([0-9]{3})[ .-]?[0-9]{4}/';
$newdata[$var]['comments'] = preg_replace($_phone_regex,'$1-****',$newdata[$var]['comments']);
$newdata[$var]['intention_other'] = preg_replace($_phone_regex,'$1-****',$newdata[$var]['intention_other']);
}
}
// Our CSV library just prints out everything in order, so we have to put the
// column labels on here ourselves
$newdata = array_merge(array(array_keys($newdata[0])), $newdata);
return $newdata;
}
/**
* Will retrieve the information used for graphing.
* @param the url parameters (unescaped)
* @return a result set
*/
function getDescriptionAndTotalsData($params)
{
// Clean parameters for inserting into SQL
$params = $this->cleanArrayForSql($params);
/* It would be nice to drop something like this in the SELECT:
*
* CONCAT(COUNT(*)/(SELECT COUNT(*) FROM our_giant_query_all_over_again)*100,'%') AS `percentage`
*/
$_query = "
SELECT
issues.description,
COUNT( DISTINCT results.id ) AS total
FROM
issues
LEFT JOIN
issues_results ON issues_results.issue_id=issues.id
LEFT JOIN results ON results.id=issues_results.result_id AND results.application_id=applications.id
JOIN applications_issues ON applications_issues.issue_id=issues.id
JOIN applications ON applications.id=applications_issues.application_id
WHERE 1=1
";
if (!empty($params['start_date'])) {
$_timestamp = strtotime($params['start_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
$_query.= " AND `results`.`created` >= '{$_date}'";
}
}
if (!empty($params['end_date'])) {
$_timestamp = strtotime($params['end_date']);
if (!($_timestamp == -1) || $_timestamp == false) {
$_date = date('Y-m-d H:i:s', $_timestamp);//sql format
$_query .= " AND `results`.`created` <= '{$_date}'";
}
}
if (!empty($params['product'])) {
// product's come in looking like:
// Mozilla Firefox 1.5.0.1
$_exp = explode(' ',urldecode($params['product']));
if(count($_exp) == 3) {
$_product = $_exp[0].' '.$_exp[1];
$_version = $_exp[2];
$_query .= " AND `applications`.`name` LIKE '{$_product}'";
$_query .= " AND `applications`.`version` LIKE '{$_version}'";
} else {
// defaults I guess?
$_query .= " AND `applications`.`name` LIKE 'Mozilla Firefox'";
$_query .= " AND `applications`.`version` LIKE '1.5'";
}
} else {
// I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired
$_query .= " AND `applications`.`name` LIKE 'Mozilla Firefox'";
$_query .= " AND `applications`.`version` LIKE '1.5'";
}
$_query .= " GROUP BY `issues`.`description`
ORDER BY `issues`.`description` DESC";
return $this->query($_query);
}
}
?>

View File

@@ -1,188 +0,0 @@
<?php
/**
* Functions that take a db result and export it to CSV.
* Usage example:
* <code>
* if ($_GET['csv'])
* {
* $res=db_query("SELECT * FROM fic_courses");
* csv_send_csv($res);
* exit;
* }
* </code>
* @package libs
* @subpackage csv
* @author Richard Faaberg <faabergr@onid.orst.edu>
* @author Mike Morgan <mike.morgan@oregonstate.edu>
*/
/**
* Use a resource or two dimensional array, then send the CSV results to user.
* @param mixed $res MySQL resource / result, or a two dimensional array
* @param string $name name of the export file
* @return bool true if file sent, false otherwise
*/
function csv_send_csv($res,$name=null)
{
// set name of the export file
$filename=(is_null($name))?'export-'.date('Y-m-d').'.csv':$name.'.csv';
// check for valid resource
if ( is_resource($res) )
{
$csv=csv_export_to_csv($res);
}
elseif( is_array($res) && !empty($res) )
{
foreach ($res as $row)
{
if ( !is_array($row) )
;
else
$csv[] = csv_array_to_csv($row)."\n";
}
}
if ( is_array($csv) )
{
// stream csv to user
header("Content-type: application/x-csv");
header('Content-disposition: inline; filename="'.$filename.'"');
header('Cache-Control: private');
header('Pragma: public');
foreach ($csv as $row)
{
echo $row;
}
return true;
}
return false;
}
/**
* Replace quotes inside of a field with double quotes, which is something CSV requires.
* @param string $string unquoted quotes
* @return string $string quoted quotes
*/
function csv_fix_quotes($string)
{
return preg_replace('/"/','""',$string);
}
/**
* Replace line breaks with commas trailed by a space.
* @param string $string string containing line breaks
* @param string string without line breaks
*/
function csv_fix_line_breaks($string)
{
return preg_replace('/(\n\r|\r)/','\n',$string);
}
/**
* Replaces instances of double quotes in a string with a single quote.
* @param string $string the string to perform the replacement on
* @return string the string with "" replaced by "
*/
function csv_unfix_quotes($string)
{
return preg_replace('/""/', '"', $string);
}
/**
* Place quotes outside of every field, which inherently solves space, line break issues.
* @param string $string
* @return string $string with quotes around it
*/
function csv_add_quotes($string)
{
return '"'.$string.'"';
}
/**
* Removes quotes from the beginning and the end of a string.
* @param string $string the string to remove the quotes from
* @return string the string, sans quotes at the beginning and end
*/
function csv_remove_quotes($string)
{
$pattern = "/^\"(.*)\"$/";
$replacement = "$1";
return preg_replace($pattern, $replacement, $string);
}
/**
* Convert an array into a CSV string with quotes around each value.
* @param array $array
* @return string the values in $array surrounded by quotes and separated by commas
*/
function csv_array_to_csv($array)
{
$csv_arr = array();
foreach ($array as $value)
{
$csv_arr[]=csv_add_quotes(csv_fix_quotes(csv_fix_line_breaks($value)));
}
$csv_string=implode(',',$csv_arr);
return $csv_string;
}
/**
* Convert a CSV string into an array.
* Please use sparingly - this creates temp files
* @param string $string the CSV string
* @return array the elements from the CSV string in an array
*/
function csv_csv_to_array($string)
{
$return = array();
$length = strlen($string);
// create a temp file and write the string to it
$tmpfname = tempnam('/tmp', 'csvlib');
$fh = fopen($tmpfname, 'w');
fwrite($fh, $string);
fclose($fh);
// open the file for csv parsing
$csvh = fopen($tmpfname, 'r');
while (($arraydata = fgetcsv($csvh, $length, ',')) !== false)
{
$return = array_merge($return, $arraydata);
}
fclose($csvh);
unlink($tmpfname);
return $return;
}
/**
* Read a CSV file into a two dimensional array
* It returns all the rows in the file, so if the first row are headers, you'd need to take care of that in the returned array
* @param string $filepath the path to the csv file
* @param string $delimiter delimiter, default to ','
* @param string $enclosure enclosure character, default to '"'
* @return &array the two dimensional array with the csv file content, or an empty if an error occured
*/
function &csv_csv_file_to_array($filepath, $delimiter=',', $enclosure='"')
{
$return = array();
if (!file_exists($filepath) || !is_readable($filepath))
return $return;
$fh =& fopen($filepath, 'r');
$size = filesize($filepath)+1;
while ($data =& fgetcsv($fh, $size, $delimiter, $enclosure))
{
$return[] = $data;
}
fclose($fh);
return $return;
}
?>

View File

@@ -1,11 +0,0 @@
<div id="t_footer">
</div>
<!-- t_wrapper -->
</div>
<!-- t_border -->
</div>
</body>
</html>

View File

@@ -1,26 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Mozilla Uninstall Survey Data</title>
<meta name="author" content="Mozilla Corporation" />
<meta name="copyright" content="Mozilla Corporation" />
<?php echo $html->charset('UTF-8'); ?>
<?php echo $html->css('screen'); ?>
<?php if (isset($include_graph_libraries) && $include_graph_libraries):
echo $javascript->link('mochikit/MochiKit.js');
echo $javascript->link('plotkit/Base.js');
echo $javascript->link('plotkit/Layout.js');
echo $javascript->link('plotkit/Canvas.js');
echo $javascript->link('plotkit/SweetCanvas.js');
endif; ?>
</head>
<body>
<div id="t_border">
<div id="t_wrapper">
<a class="skipnav" href="#t_content">Skip Navigation Links</a>
<div id="t_header">
<div id="t_title">
<h1>Mozilla Firefox Uninstall Survey Data</h1>
</div>
</div>

View File

@@ -1,6 +0,0 @@
<div id="t_nav">
<ul>
<li><?php echo $html->link('Results', '/results'); ?></li>
<li><?php echo $html->link('Comments', '/results/comments'); ?></li>
</ul>
</div>

View File

@@ -1,36 +0,0 @@
<?php
/**
* When we're exporting data, we end up having to pass all the parameters
* via $_GET. Since we're doing this a few times per page (each graph, and the
* export hrefs) this helper should make that easier.
*/
class ExportHelper
{
/**
* Method to collect the current $_GET parameters and build another string from
* them.
*
* @param string cake URL that you want prepended to the result. eg: reports/graph/
* @param array the array of GET variables to add. These need to be
* pre-sanitized to print in html! This is designed to
* be used from the calling controller like:
* $params = $this->sanitize->html($url_parameters);
* @param string string to put between url and arguments (probably either '?' or
* '&')
* @param array array of strings which will be ignored
* @return string string with the url variables appeneded to it
*/
function buildUrlString($url, $params, $seperator='?', $ignore=array('url'))
{
$arguments = '';
foreach ($params as $var => $val) {
if (!in_array($var, $ignore)) {
$arguments .= empty($arguments) ? "{$var}={$val}" : "&amp;{$var}={$val}";
}
}
return "{$url}{$seperator}{$arguments}";
}
}
?>

View File

@@ -1,196 +0,0 @@
<?php
class PaginationHelper {
var $helpers = array('Html','Ajax');
var $_pageDetails = array();
var $link = '';
var $show = array();
var $page;
var $style;
/**
* Sets the default pagination options.
*
* @param array $paging an array detailing the page options
*/
function setPaging($paging)
{
if(!empty($paging))
{
$this->link = $paging['link'];
$this->show = $paging['show'];
$this->page = $paging['page'];
$this->style = $paging['style'];
$pageCount = ceil($paging['count'] / $paging['limit'] );
$this->_pageDetails = array(
'page'=>$paging['page'],
'recordCount'=>$paging['count'],
'pageCount' =>$pageCount,
'nextPage'=> ($paging['page'] < $pageCount) ? $paging['page']+1 : '',
'previousPage'=> ($paging['page']>1) ? $paging['page']-1 : '',
'limit'=>$paging['limit']
);
return true;
}
return false;
}
/**
* Displays limits for the query
*
* @param string $text - text to display before limits
* @param string $separator - display a separate between limits
*
**/
function show($text=null, $separator=null)
{
if (empty($this->_pageDetails)) { return false; }
if ( !empty($this->_pageDetails['recordCount']) )
{
$t = '';
if(is_array($this->show))
{
$t = $text.$separator;
foreach($this->show as $value)
{
$link = preg_replace('/show=(.*?)&/','show='.$value.'&',$this->link);
if($this->_pageDetails['limit'] == $value)
{
$t .= '<em>'.$value.'</em>'.$separator;
}
else
{
if($this->style == 'ajax')
{
$t .= $this->Ajax->linkToRemote($value, array("fallback"=>$this->action."#","url" => $link.$this->_pageDetails['page'],"update" => "ajax_update","method"=>"get")).$separator;
}
else
{
$t .= $this->Html->link($value,$link.$this->_pageDetails['page']).$separator;
}
}
}
}
return $t;
}
return false;
}
/**
* Displays current result information
*
* @param string $text - text to preceeding the number of results
*
**/
function result($text)
{
if (empty($this->_pageDetails)) { return false; }
if ( !empty($this->_pageDetails['recordCount']) )
{
if($this->_pageDetails['recordCount'] > $this->_pageDetails['limit'])
{
$start_row = $this->_pageDetails['page'] > 1 ? (($this->_pageDetails['page']-1)*$this->_pageDetails['limit'])+1:'1';
$end_row = ($this->_pageDetails['recordCount'] < ($start_row + $this->_pageDetails['limit']-1)) ? $this->_pageDetails['recordCount'] : ($start_row + $this->_pageDetails['limit']-1);
$t = $text.$start_row.'-'.$end_row.' of '.$this->_pageDetails['recordCount'];
}
else
{
$t = $text.$this->_pageDetails['recordCount'];
}
return $t;
}
return false;
}
/**
* Returns a list of page numbers separated by $separator
*
* @param string $separator - defaults to null
*
**/
function pageNumbers($separator=null)
{
if (empty($this->_pageDetails) || $this->_pageDetails['pageCount'] == 1) { return false; }
$t = array();
$text = '';
$pc = 1;
do
{
if($pc == $this->_pageDetails['page'])
{
$text = '<em>'.$pc.'</em>';
}
else
{
if($this->style == 'ajax')
{
$text = $this->Ajax->linkToRemote($pc, array("fallback"=>$this->action."#","url" =>$this->link.$pc,"update" => "ajax_update","method"=>"get"));
}
else
{
$text = $this->Html->link($pc,$this->link.$pc);
}
}
$t[] = $text;
$pc++;
}
while ($pc<=$this->_pageDetails['pageCount']);
$t = implode($separator, $t);
return $t;
}
/**
* Displays a link to the previous page, where the page doesn't exist then
* display the $text
*
* @param string $text - text display: defaults to next
*
**/
function prevPage($text='prev')
{
if (empty($this->_pageDetails)) { return false; }
if ( !empty($this->_pageDetails['previousPage']) )
{
if($this->style == 'ajax')
{
$t = $this->Ajax->linkToRemote($text, array("fallback"=>$this->action."#","url" => $this->link.$this->_pageDetails['previousPage'],"update" => "ajax_update","method"=>"get"));
}
else
{
$t = $this->Html->link($text,$this->link.$this->_pageDetails['previousPage']);
}
return $t;
}
return false;
}
/**
* Displays a link to the next page, where the page doesn't exist then
* display the $text
*
* @param string $text - text to display: defaults to next
*
**/
function nextPage($text='next')
{
if (empty($this->_pageDetails)) { return false; }
if (!empty($this->_pageDetails['nextPage']))
{
if($this->style == 'ajax')
{
$t = $this->Ajax->linkToRemote($text, array("fallback"=>$this->action."#","url" => $this->link.$this->_pageDetails['nextPage'],"update" => "ajax_update","method"=>"get"));
}
else
{
$t = $this->Html->link($text,$this->link.$this->_pageDetails['nextPage']);
}
return $t;
}
return false;
}
}
?>

View File

@@ -1,8 +0,0 @@
<?php echo $this->renderElement( 'header' ); ?>
<?php echo $this->renderElement( 'nav' ); ?>
<div id="t_content_container">
<div id="t_content">
<?php echo $content_for_layout;?>
</div>
</div>
<?php echo $this->renderElement( 'footer'); ?>

View File

@@ -1,51 +0,0 @@
<h1><?php echo Inflector::humanize($this->name); ?></h1>
<div id="queryform">
<?php echo $html->formTag('/results/comments/','get'); ?>
<span>
<label for="start_date">Start Date:</label>
<input type="text" name="start_date" id="start_date" value="<? echo isset($url_params['start_date']) ? $url_params['start_date'] : ''; ?>" />
<label for="end_date">End Date:</label>
<input type="text" name="end_date" id="end_date" value="<? echo isset($url_params['end_date']) ? $url_params['end_date'] : ''; ?>" />
</span>
<label for="product">Product:</label>
<select name="product" id="product">
<?php
foreach ($products as $select) :
$product = $select['applications']['name'].' '.$select['applications']['version'];
$selected = ($product == trim($url_params['product'])) ? ' selected="selected" ' : '';
?>
<option<?=$selected?>><?=$product?></option>
<?php endforeach; ?>
</select>
<input type="submit" name="submit" id="submit" value="Go" />
</form>
<br />
<span><small>Date format is yyyy-mm-dd. A blank date will use the largest possible range.</small></span>
</div>
<?php if (!empty($commentsData)) : ?>
<table class="comments" summary="User submitted comments.">
<tr><th>Date Recorded</th><th>Comment</th></tr>
<?php foreach ($commentsData as $var => $val) : ?>
<tr><td><?php echo $time->niceShort($val['Result']['created']); ?></td><td><?php echo nl2br(htmlspecialchars($val['Result']['comments'])); ?></td></tr>
<?php endforeach; ?>
</table>
<?php if($pagination->setPaging($paging)):?>
<ul id="page-numbers">
<li><?php echo $pagination->show('Show ', ' '); ?></li>
<li><?php echo $pagination->result('Results: '); ?></li>
<li><?php echo $pagination->pageNumbers(' '); ?></li>
</ul>
<?php endif;?>
<?php else: ?>
<div id="notice">There is no data available for your parameters. Please review your search criteria.</div>
<?php endif; ?>

View File

@@ -1,91 +0,0 @@
<h1><?php echo Inflector::humanize($this->name); ?></h1>
<div id="queryform">
<?php echo $html->formTag('/results/','get'); ?>
<span>
<label for="start_date">Start Date:</label>
<input type="text" name="start_date" id="start_date" value="<? echo isset($url_params['start_date']) ? $url_params['start_date'] : ''; ?>" />
<label for="end_date">End Date:</label>
<input type="text" name="end_date" id="end_date" value="<? echo isset($url_params['end_date']) ? $url_params['end_date'] : ''; ?>" />
</span>
<label for="product">Product:</label>
<select name="product" id="product">
<?php
foreach ($products as $select) :
$product = $select['applications']['name'].' '.$select['applications']['version'];
$selected = ($product == trim($url_params['product'])) ? ' selected="selected" ' : '';
?>
<option<?=$selected?>><?=$product?></option>
<?php endforeach; ?>
</select>
<input type="submit" name="submit" id="submit" value="Go" />
</form>
<br />
<span><small>Date format is yyyy-mm-dd. A blank date will use the largest possible range.</small></span>
</div>
<?php if (!empty($descriptionAndTotalsData)) : ?>
<?php
/* Prepare data for graphing */
$_dataset = '';
$_count = 0;
$_total = 0;
/* We've got to do 2 loops here because I need the totals to calculate
* percentages.
*/
foreach ($descriptionAndTotalsData as $var => $val) {
$_total += $val[0]['total'];
}
foreach ($descriptionAndTotalsData as $var => $val) {
// We're putting this in a js string, so escape the double quotes
$_description = str_replace('"','\"',$val['issues']['description']);
$_percentage = intval(($val[0]['total'] / $_total)*100);
$_dataset .= "[{$_count}, {$val[0]['total']}, \"{$_description} (n={$val[0]['total']}, {$_percentage}%)\"], ";
$_count++;
}
$_dataset = "[{$_dataset}]";
?>
<div id="graphcontainer">
<canvas id="graph" height="400" width="800">
<table class="results" summary="Firefox Uninstallation results.">
<tr><th>Reason for uninstalling</th><th>Total</th></tr>
<?php foreach ($descriptionAndTotalsData as $var => $val) : ?>
<tr><td><?=$val['issues']['description']?></td><td><?=$val[0]['total']?></td></tr>
<?php endforeach; ?>
</table>
</canvas>
</div>
<script type="text/javascript">
var layout = new PlotKit.Layout("bar", {"barOrientation":"horizontal"});
layout.addDataset("results", <?=$_dataset?>);
layout.evaluate();
var canvas = MochiKit.DOM.getElement("graph");
var plotter = new PlotKit.SweetCanvasRenderer(canvas, layout,
{
"drawYAxis" : false,
"drawXAxis" : false,
"backgroundColor" : Color.fromHexString('#78c'),
"strokeColor" : Color.fromHexString('#777'),
"strokeWidth" : 1.0,
"enableEvents" : false
});
plotter.render();
</script>
<div id="exportbox">
<a href="<?php echo $export->buildUrlString('results/comments/',$url_params); ?>">View Any Associated Comments»</a><br />
<a href="<?php echo $export->buildUrlString('results/csv/',$url_params); ?>">Download this Data»</a>
</div>
<?php else: ?>
<div id="notice">There is no data available for your parameters. Please review your search criteria.</div>
<?php endif; ?>

View File

@@ -1,6 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

View File

@@ -1,116 +0,0 @@
<?php
/* SVN FILE: $Id: css.php,v 1.1.1.1 2006-05-24 19:14:24 uid815 Exp $ */
/**
* Short description for file.
*
* Long description for file
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
* Copyright (c) 2006, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
* @package cake
* @subpackage cake.app.webroot
* @since CakePHP v 0.2.9
* @version $Revision: 1.1.1.1 $
* @modifiedby $LastChangedBy: phpnut $
* @lastmodified $Date: 2006-05-24 19:14:24 $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Enter description here...
*/
require(CONFIGS.'paths.php');
require(CAKE.'basics.php');
require(LIBS.'folder.php');
require(LIBS.'file.php');
require(LIBS.'legacy.php');
/**
* Enter description here...
*
* @param unknown_type $path
* @param unknown_type $name
* @return unknown
*/
function make_clean_css ($path, $name)
{
require(VENDORS.'csspp'.DS.'csspp.php');
$data = file_get_contents($path);
$csspp = new csspp();
$output = $csspp->compress($data);
$ratio = 100-(round(strlen($output)/strlen($data), 3)*100);
$output = " /* file: $name, ratio: $ratio% */ " . $output;
return $output;
}
/**
* Enter description here...
*
* @param unknown_type $path
* @param unknown_type $content
* @return unknown
*/
function write_css_cache ($path, $content)
{
if (!is_dir(dirname($path)))
mkdir(dirname($path));
$cache = new File($path);
return $cache->write($content);
}
if (preg_match('|\.\.|', $url) || !preg_match('|^ccss/(.+)$|i', $url, $regs))
die('Wrong file name.');
$filename = 'css/'.$regs[1];
$filepath = CSS.$regs[1];
$cachepath = CACHE.'css'.DS.str_replace(array('/','\\'), '-', $regs[1]);
if (!file_exists($filepath))
die('Wrong file name.');
if (file_exists($cachepath))
{
$templateModified = filemtime($filepath);
$cacheModified = filemtime($cachepath);
if ($templateModified > $cacheModified)
{
$output = make_clean_css ($filepath, $filename);
write_css_cache ($cachepath, $output);
}
else
{
$output = file_get_contents($cachepath);
}
}
else
{
$output = make_clean_css ($filepath, $filename);
write_css_cache ($cachepath, $output);
}
header("Date: ".date("D, j M Y G:i:s ", $templateModified).'GMT');
header("Content-Type: text/css");
header("Expires: ".gmdate("D, j M Y H:i:s", time()+DAY)." GMT");
header("Cache-Control: cache");// HTTP/1.1
header("Pragma: cache");// HTTP/1.0
print $output;
?>

View File

@@ -1,195 +0,0 @@
/* CSS Document */
@import url("cake.forms.css");
* {
padding:0;
margin:0;
}
body {
font: 76% Verdana, Arial, Sans-serif;
color: #333;
}
img {
border:0;
}
#wrapper {
text-align:left;
}
#header {
height: 101px;
background: #0D5087 url(../img/cake.header_bg.png) repeat-x left top;
border-bottom: 1px solid #000;
}
#content {
min-height:400px;
background-color: #fff;
padding:2em 4em;
}
#footer {
text-align:center;
padding:1em 0;
font-size:smaller;
border-top:1px solid #333;
background-color: #063260;
color:#fff;
line-height:1.5;
}
#footer a {
color:#fff;
}
h1, h2, h3, h4 {
padding-bottom:0.5em;
}
h1 {
font-family:"Trebuchet MS",Verdana,Arial,Sans-serif;
}
h1, a {
color:#DB8101;
}
h1 em, a em {
color:#008BCC;
font-style: normal;
}
ul.colored a em
h2 {
font-style: italic;
font-weight: bold;
color:#666;
}
a:hover, a:hover em {
color:#A22424;
text-decoration:none;
}
#content p, #content ul, #content ol {
line-height:1.5;
padding-bottom:1em;
}
ul, ol {
margin-left:3em;
}
/* tables */
table {
width: 100%;
background-color: #fff;
border: 1px solid #333;
clear:both;
margin: 0 0 2em 0;
white-space: normal;
}
th {
background-color: #ccc;
border-top: 1px solid #fff;
border-right: 1px solid #666;
border-bottom: 1px solid #666;
text-align: center;
padding:3px;
}
table tr td {
border-right: 1px solid #ccc;
padding:4px 4px;
vertical-align:top;
text-align: center;
}
table tr.altRow td {
background: #f4f4f4;
}
/* scaffold show */
div.related {
clear:both;
display:block;
}
dl {
line-height:2em;
margin:1em;
}
dt {
font-weight: bold;
vertical-align:top;
}
dd {
margin-left:10em;
margin-top:-2em;
vertical-align:top;
}
/* scaffold buttons */
.notice {
color: #DB8101;
background-color: #ddd;
display: block;
padding: 1em;
}
.tip {
color: #DB8101;
background-color: #ddd;
display: block;
padding: 1em;
}
ul.actions {
list-style: none;
text-align:left;
margin:2em 0;
padding: 0;
}
ul.actions li {
margin-left:1em;
list-style: none;
display: inline;
}
ul.actions li a, ul.actions li input {
padding: 2px 12px;
color: #DB8101;
background-color:#ccc;
text-decoration: none;
border: 1px solid #666;
line-height: 24px;
font-weight: bold;
text-align:center;
text-decoration: none;
}
ul.actions li a:hover {
color: #DB8101;
background-color:#fff;
text-decoration: none;
}
td.listactions {
width: 14em;
text-align: center;
white-space: nowrap;
}
td.listactions a {
padding: 0px 8px;
text-align:center;
font-weight: bold;
color: #DB8101;
background-color:#ccc;
text-decoration: none;
border: 1px solid #666;
white-space: nowrap;
}
td.listactions a:hover {
color: #fff;
background-color:#DB8101;
}
/* index links */
ul.colored a {
}
ul.colored a em {
}
a {
font-weight: bold;
}

View File

@@ -1,279 +0,0 @@
/* form.css */
form {
margin: 0 4px;
font-size: 120%;
border-width: 0px 0px 0px 0px;
border-style: solid;
border-color: #DB8101;
}
form fieldset {
font-size: 100%;
border-color: #000000;
border-width: 1px 0px 0px 0px;
border-style: solid none none none;
padding: 10px;
}
form fieldset legend {
font-size: 150%;
font-weight: normal;
color: #000;
padding: 0px 5px;
}
label {
font-size: 150%;
}
label u {
font-style: normal;
text-decoration: underline;
}
input, select, textarea {
font-family: Tahoma, Arial, sans-serif;
font-size: 100%;
color: #000;
}
textarea {
overflow: auto;
}
form div {
clear: left;
display: block;
margin: 5px 0px 0px 0px;
padding: 1px 3px;
}
form fieldset div.notes {
float: right;
width: 158px;
height: auto;
margin: 0px 0px 10px 10px;
padding: 5px;
border: 1px solid #666;
background-color: #ffffe1;
color: #666;
font-size: 88%;
}
form fieldset div.notes h4 {
background-image: url(/images/icon_info.gif);
background-repeat: no-repeat;
background-position: top left;
padding: 3px 0px 3px 27px;
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: #666;
color: #666;
font-size: 110%;
}
form fieldset div.notes p {
margin: 0em 0em 1.2em 0em;
color: #666;
}
form fieldset div.notes p.last {
margin: 0em;
}
form div fieldset {
clear: none;
border-width: 0px 1px 0px 1px;
border-style: solid;
border-color: #666;
margin: 0px 0px 0px 142px;
padding: 0px 5px 5px 5px;
}
form div fieldset legend {
font-size: 100%;
padding: 0px 3px 0px 9px;
}
form div.required fieldset legend {
font-weight: bold;
}
form div label {
display: block;
float: left;
width: 200px;
background-color: #f4f4f4;
font-size: 16px;
padding: 3px 5px;
margin: 0px 0px 5px 0px;
text-align: right;
}
form div.optional label, label.optional {
font-weight: normal;
}
form div.required label, label.required {
font-weight: bold;
}
form div label.labelCheckbox, form div label.labelRadio {
float: none;
display: block;
margin: 0px 0px 5px 142px;
text-align: left;
}
form div fieldset label.labelCheckbox, form div fieldset label.labelRadio {
margin: 0px 0px 5px 0px;
}
p.error {
color: #DB8101;
background-color: #DBA941;
font-size: 14px;
padding: 1em;
}
form div input, form div select, form div textarea {
padding: 1px 3px;
margin: 0px 0px 0px 0px;
}
form div input.inputFile {
width: 211px;
}
form div select.selectOne, form div select.selectMultiple {
width: 211px;
padding: 1px 3px;
}
form div input.inputCheckbox, form div input.inputRadio, input.inputCheckbox, input.inputRadio {
display: inline;
height: 14px;
width: 14px;
background-color: transparent;
border-width: 0px;
}
form div.submit {
padding: 0px 0px 0px 140px;
clear:both;
display:block;
}
div.submit input {
padding: 2px 12px;
color: #DB8101;
background-color:#ccc;
text-decoration: none;
border: 1px solid #666;
font-size: 14px;
font-weight: bold;
text-align:center;
text-decoration: none;
width: auto;
}
div.submit input:hover {
padding: 2px 12px;
color: #fff;
background-color:#DB8101;
text-decoration: none;
border: 1px solid #666;
font-size: 14px;
font-weight: bold;
text-align:center;
text-decoration: none;
width: auto;
}
form div.submit div input.inputSubmit, form div.submit div input.inputButton {
float: right;
margin: 0px 0px 0px 5px;
}
form div small {
display: block;
margin: 0px 0px 5px 142px;
padding: 1px 3px;
font-size: 88%;
}
/* form.import.css */
form fieldset legend {
line-height: 150%;
}
form input, form select, form textarea {
background-color: #fff;
}
div.optional label:before {
content: '';
}
div.required label:before {
content: '';
}
form div label.labelCheckbox, form div label.labelRadio, label.labelCheckbox, label.labelRadio {
display: block;
width: 190px;
padding: 4px 0px 0px 18px;
text-indent: -18px;
line-height: 120%;
}
form div label.labelCheckbox input.inputCheckbox, form div label.labelRadio input.inputRadio, label.labelCheckbox input.inputCheckbox, label.labelRadio input.inputRadio {
margin: 0;
}
form div fieldset input.inputText, form div fieldset input.inputPassword, form div fieldset input.inputFile, form div fieldset textarea.inputTextarea {
width: 160px;
margin: 0px 0px 0px 18px;
}
form div label.compact {
display: inline;
width: auto;
padding: 4px 10px 0px 0px;
text-indent: 0px;
margin: 0;
}
form div.wide label {
float: none;
display: block;
}
form div label.wide {
width: 348px;
}
form div.wide input.inputText, form div.wide input.inputPassword, form div.wide input.inputFile, form div.wide select, form div.wide textarea {
width: 344px;
margin: 0;
}
form div.notes p, form div small {
line-height: 125%;
}
form div.wide small {
margin: 0px 0px 5px 0px;
}
div.date select {
width:auto;
}
select.autoWidth {
width:auto;
}
option {
padding-left:1em;
}

View File

@@ -1,9 +0,0 @@
form div,
form div label.labelCheckbox, form div label.labelRadio,
form div small,
form div label.labelCheckbox, form div label.labelRadio, label.labelCheckbox, label.labelRadio {
height: expression('1%');
}
form div fieldset input.inputText, form div fieldset input.inputPassword, form div fieldset input.inputFile, form div fieldset textarea.inputTextarea {
margin: expression('0px 0px 0px -124px');
}

View File

@@ -1,133 +0,0 @@
/* Screen Stylesheet */
/* This is temporary while we use the scaffolding */
@import url("cake.default.css");
table { clear:none; }
/* CORE STYLES */
body{
background-color:#788;
}
div#t_title{
background-color:#ddd;
height:50px;
padding-left:8px;
}
div#t_title h1{
font-size:1.7em;
padding-top:10px;
color:#677;
}
div#t_content_container{
}
div#t_content{
padding: 20px 10px 100px 10px;
margin-left:151px;
background-image:url('../img/bg.jpg');
background-position:bottom right;
background-repeat:no-repeat;
min-height:200px;
}
div#t_wrapper{
margin:0px;
padding:0px;
background-color:#fff;
}
div#t_border{
border-left:1px solid #111;
border-right:1px solid #111;
border-bottom:1px solid #111;
margin:0px 30px 0px 30px;
}
div#t_nav {
padding:20px 0px 50px 0px;
float:left;
width:151px;
}
div#t_nav ul {
list-style-type:none;
padding:2px 0px 0px 0px;
margin:0px;
}
div#t_nav ul li{
padding:2px 0px 2px 0px;
}
div#t_nav ul li a{
text-decoration:none;
font-size:.8em;
padding:6px 0px 6px 8px;
margin:0px 0px 0px 0px;
color:#566;
border:none;
}
div#t_nav ul li a:hover{
color:#f80;
}
div#t_nav h3 {
padding:5px 0px 0px 5px;
font-size:.9em;
}
div#t_footer {
clear:both;
border-top:1px solid #111;
}
div#t_content h1 {
margin-bottom:100px;
font-size:1.6em;
}
a.skipnav {
position:absolute;
visibility:hidden;
}
div#queryform {
margin: 5px 20px 5px 20px;
padding-bottom:20px;
width:700px;
}
div#queryform label {
font-size:.8em;
font-weight:bold;
}
div#queryform input[type="text"]{
width:7em;
}
div#exportbox {
}
/* Classes for graph/table data. (Generally just padding) */
.results {
padding: 5px 5px 5px 5px;
margin: 5px 5px 5px 5px;
}
div#notice {
background-color:#ecc;
border:1px dashed #daa;
text-align:center;
padding:6px 6px 6px 6px;
margin:6px 20px 6px 20px;
}
table.comments tr {
}
table.comments tr td{
text-align:left;
border-bottom: 1px solid #222;
}
div.bar_chart_label {
position: absolute;
text-align: left;
font-size: small;
color: #fff;
overflow: hidden;
z-index: 10;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,244 +0,0 @@
2006-04-29 v1.3.1 (bug fix release)
- Fix sendXMLHttpRequest sendContent regression
- Internet Explorer fix in MochiKit.Logging (printfire exception)
- Internet Explorer XMLHttpRequest object leak fixed in MochiKit.Async
2006-04-26 v1.3 "warp zone"
- IMPORTANT: Renamed MochiKit.Base.forward to forwardCall (for export)
- IMPORTANT: Renamed MochiKit.Base.find to findValue (for export)
- New MochiKit.Base.method as a convenience form of bind that takes the
object before the method
- New MochiKit.Base.flattenArguments for flattening a list of arguments to
a single Array
- Refactored MochiRegExp example to use MochiKit.Signal
- New key_events example demonstrating use of MochiKit.Signal's key handling
capabilities.
- MochiKit.DOM.createDOM API change for convenience: if attrs is a string,
null is used and the string will be considered the first node. This
allows for the more natural P("foo") rather than P(null, "foo").
- MochiKit Interpreter example refactored to use MochiKit.Signal and now
provides multi-line input and a help() function to get MochiKit function
signature from the documentation.
- Native Console Logging for the default MochiKit.Logging logger
- New MochiKit.Async.DeferredList, gatherResults, maybeDeferred
- New MochiKit.Signal example: draggable
- Added sanity checking to Deferred to ensure that errors happen when chaining
is used incorrectly
- Opera sendXMLHttpRequest fix (sends empty string instead of null by default)
- Fix a bug in MochiKit.Color that incorrectly generated hex colors for
component values smaller than 16/255.
- Fix a bug in MochiKit.Logging that prevented logs from being capped at a
maximum size
- MochiKit.Async.Deferred will now wrap thrown objects that are not instanceof
Error, so that the errback chain is used instead of the callback chain.
- MochiKit.DOM.appendChildNodes and associated functions now append iterables
in the correct order.
- New MochiKit-based SimpleTest test runner as a replacement for Test.Simple
- MochiKit.Base.isNull no longer matches undefined
- example doctypes changed to HTML4
- isDateLike no longer throws error on null
- New MochiKit.Signal module, modeled after the slot/signal mechanism in Qt
- updated elementDimensions to calculate width from offsetWidth instead
of clientWidth
- formContents now works with FORM tags that have a name attribute
- Documentation now uses MochiKit to generate a function index
2006-01-26 v1.2 "the ocho"
- Fixed MochiKit.Color.Color.lighterColorWithLevel
- Added new MochiKit.Base.findIdentical function to find the index of an
element in an Array-like object. Uses === for identity comparison.
- Added new MochiKit.Base.find function to find the index of an element in
an Array-like object. Uses compare for rich comparison.
- MochiKit.Base.bind will accept a string for func, which will be immediately
looked up as self[func].
- MochiKit.DOM.formContents no longer skips empty form elements for Zope
compatibility
- MochiKit.Iter.forEach will now catch StopIteration to break
- New MochiKit.DOM.elementDimensions(element) for determining the width and
height of an element in the document
- MochiKit.DOM's initialization is now compatible with
HTMLUnit + JWebUnit + Rhino
- MochiKit.LoggingPane will now re-use a ``_MochiKit_LoggingPane`` DIV element
currently in the document instead of always creating one.
- MochiKit.Base now has operator.mul
- MochiKit.DOM.formContents correctly handles unchecked checkboxes that have
a custom value attribute
- Added new MochiKit.Color constructors fromComputedStyle and fromText
- MochiKit.DOM.setNodeAttribute should work now
- MochiKit.DOM now has a workaround for an IE bug when setting the style
property to a string
- MochiKit.DOM.createDOM now has workarounds for IE bugs when setting the
name and for properties
- MochiKit.DOM.scrapeText now walks the DOM tree in-order
- MochiKit.LoggingPane now sanitizes the window name to work around IE bug
- MochiKit.DOM now translates usemap to useMap to work around IE bug
- MochiKit.Logging is now resistant to Prototype's dumb Object.prototype hacks
- Added new MochiKit.DOM documentation on element visibility
- New MochiKit.DOM.elementPosition(element[, relativeTo={x: 0, y: 0}])
for determining the position of an element in the document
- Added new MochiKit.DOM createDOMFunc aliases: CANVAS, STRONG
2005-11-14 v1.1
- Fixed a bug in numberFormatter with large numbers
- Massively overhauled documentation
- Fast-path for primitives in MochiKit.Base.compare
- New groupby and groupby_as_array in MochiKit.Iter
- Added iterator factory adapter for objects that implement iterateNext()
- Fixed isoTimestamp to handle timestamps with time zone correctly
- Added new MochiKit.DOM createDOMFunc aliases: SELECT, OPTION, OPTGROUP,
LEGEND, FIELDSET
- New MochiKit.DOM formContents and enhancement to queryString to support it
- Updated view_source example to use dp.SyntaxHighlighter 1.3.0
- MochiKit.LoggingPane now uses named windows based on the URL so that
a given URL will get the same LoggingPane window after a reload
(at the same position, etc.)
- MochiKit.DOM now has currentWindow() and currentDocument() context
variables that are set with withWindow() and withDocument(). These
context variables affect all MochiKit.DOM functionality (getElement,
createDOM, etc.)
- MochiKit.Base.items will now catch and ignore exceptions for properties
that are enumerable but not accessible (e.g. permission denied)
- MochiKit.Async.Deferred's addCallback/addErrback/addBoth
now accept additional arguments that are used to create a partially
applied function. This differs from Twisted in that the callback/errback
result becomes the *last* argument, not the first when this feature
is used.
- MochiKit.Async's doSimpleXMLHttpRequest will now accept additional
arguments which are used to create a GET query string
- Did some refactoring to reduce the footprint of MochiKit by a few
kilobytes
- escapeHTML to longer escapes ' (apos) and now uses
String.replace instead of iterating over every char.
- Added DeferredLock to Async
- Renamed getElementsComputedStyle to computedStyle and moved
it from MochiKit.Visual to MochiKit.DOM
- Moved all color support out of MochiKit.Visual and into MochiKit.Color
- Fixed range() to accept a negative step
- New alias to MochiKit.swapDOM called removeElement
- New MochiKit.DOM.setNodeAttribute(node, attr, value) which sets
an attribute on a node without raising, roughly equivalent to:
updateNodeAttributes(node, {attr: value})
- New MochiKit.DOM.getNodeAttribute(node, attr) which gets the value of
a node's attribute or returns null without raising
- Fixed a potential IE memory leak if using MochiKit.DOM.addToCallStack
directly (addLoadEvent did not leak, since it clears the handler)
2005-10-24 v1.0
- New interpreter example that shows usage of MochiKit.DOM to make
an interactive JavaScript interpreter
- New MochiKit.LoggingPane for use with the MochiKit.Logging
debuggingBookmarklet, with logging_pane example to show its usage
- New mochiregexp example that demonstrates MochiKit.DOM and MochiKit.Async
in order to provide a live regular expression matching tool
- Added advanced number formatting capabilities to MochiKit.Format:
numberFormatter(pattern, placeholder="", locale="default") and
formatLocale(locale="default")
- Added updatetree(self, obj[, ...]) to MochiKit.Base, and changed
MochiKit.DOM's updateNodeAttributes(node, attrs) to use it when appropiate.
- Added new MochiKit.DOM createDOMFunc aliases: BUTTON, TT, PRE
- Added truncToFixed(aNumber, precision) and roundToFixed(aNumber, precision)
to MochiKit.Format
- MochiKit.DateTime can now handle full ISO 8601 timestamps, specifically
isoTimestamp(isoString) will convert them to Date objects, and
toISOTimestamp(date, true) will return an ISO 8601 timestamp in UTC
- Fixed missing errback for sendXMLHttpRequest when the server does not
respond
- Fixed infinite recusion bug when using roundClass("DIV", ...)
- Fixed a bug in MochiKit.Async wait (and callLater) that prevented them
from being cancelled properly
- Workaround in MochiKit.Base bind (and partial) for functions that don't
have an apply method, such as alert
- Reliably return null from the string parsing/manipulation functions if
the input can't be coerced to a string (s + "") or the input makes no sense;
e.g. isoTimestamp(null) and isoTimestamp("") return null
2005-10-08 v0.90
- Fixed ISO compliance with toISODate
- Added missing operator.sub
- Placated Mozilla's strict warnings a bit
- Added JSON serialization and unserialization support to MochiKit.Base:
serializeJSON, evalJSON, registerJSON. This is very similar to the repr
API.
- Fixed a bug in the script loader that failed in some scenarios when a script
tag did not have a "src" attribute (thanks Ian!)
- Added new MochiKit.DOM createDOMFunc aliases: H1, H2, H3, BR, HR, TEXTAREA,
P, FORM
- Use encodeURIComponent / decodeURIComponent for MochiKit.Base urlEncode
and parseQueryString, when available.
2005-08-12 v0.80
- Source highlighting in all examples, moved to a view-source example
- Added some experimental syntax highlighting for the Rounded Corners example,
via the LGPL dp.SyntaxHighlighter 1.2.0 now included in examples/common/lib
- Use an indirect binding for the logger conveniences, so that the global
logger could be replaced by setting MochiKit.Logger.logger to something else
(though an observer is probably a better choice).
- Allow MochiKit.DOM.getElementsByTagAndClassName to take a string for parent,
which will be looked up with getElement
- Fixed bug in MochiKit.Color.fromBackground (was using node.parent instead of
node.parentNode)
- Consider a 304 (NOT_MODIFIED) response from XMLHttpRequest to be success
- Disabled Mozilla map(...) fast-path due to Deer Park compatibility issues
- Possible workaround for Safari issue with swapDOM, where it would get
confused because two elements were in the DOM at the same time with the
same id
- Added missing THEAD convenience function to MochiKit.DOM
- Added lstrip, rstrip, strip to MochiKit.Format
- Added updateNodeAttributes, appendChildNodes, replaceChildNodes to
MochiKit.DOM
- MochiKit.Iter.iextend now has a fast-path for array-like objects
- Added HSV color space support to MochiKit.Visual
- Fixed a bug in the sortable_tables example, it now converts types
correctly
- Fixed a bug where MochiKit.DOM referenced MochiKit.Iter.next from global
scope
2005-08-04 v0.70
- New ajax_tables example, which shows off XMLHttpRequest, ajax, json, and
a little TAL-ish DOM templating attribute language.
- sendXMLHttpRequest and functions that use it (loadJSONDoc, etc.) no longer
ignore requests with status == 0, which seems to happen for cached or local
requests
- Added sendXMLHttpRequest to MochiKit.Async.EXPORT, d'oh.
- Changed scrapeText API to return a string by default. This is API-breaking!
It was dumb to have the default return value be the form you almost never
want. Sorry.
- Added special form to swapDOM(dest, src). If src is null, dest is removed
(where previously you'd likely get a DOM exception).
- Added three new functions to MochiKit.Base for dealing with URL query
strings: urlEncode, queryString, parseQueryString
- MochiKit.DOM.createDOM will now use attr[k] = v for all browsers if the name
starts with "on" (e.g. "onclick"). If v is a string, it will set it to
new Function(v).
- Another workaround for Internet "worst browser ever" Explorer's setAttribute
usage in MochiKit.DOM.createDOM (checked -> defaultChecked).
- Added UL, OL, LI convenience createDOM aliases to MochiKit.DOM
- Packing is now done by Dojo's custom Rhino interpreter, so it's much smaller
now!
2005-07-29 v0.60
- Beefed up the MochiKit.DOM test suite
- Fixed return value for MochiKit.DOM.swapElementClass, could return
false unexpectedly before
- Added an optional "parent" argument to
MochiKit.DOM.getElementsByTagAndClassName
- Added a "packed" version in packed/MochiKit/MochiKit.js
- Changed build script to rewrite the URLs in tests to account for the
JSAN-required reorganization
- MochiKit.Compat to potentially work around IE 5.5 issues
(5.0 still not supported). Test.Simple doesn't seem to work there,
though.
- Several minor documentation corrections
2005-07-27 v0.50
- Initial Release

View File

@@ -1,69 +0,0 @@
MochiKit is dual-licensed software. It is available under the terms of the
MIT License, or the Academic Free License version 2.1. The full text of
each license is included below.
MIT License
===========
Copyright (c) 2005 Bob Ippolito. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Academic Free License v. 2.1
============================
Copyright (c) 2005 Bob Ippolito. All rights reserved.
This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
Licensed under the Academic Free License version 2.1
1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
a) to reproduce the Original Work in copies;
b) to prepare derivative works ("Derivative Works") based upon the Original Work;
c) to distribute copies of the Original Work and Derivative Works to the public;
d) to perform the Original Work publicly; and
e) to display the Original Work publicly.
2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
5) This section intentionally omitted.
6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions.
10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.

View File

@@ -1,115 +0,0 @@
Changes
META.json
MANIFEST This list of files
LICENSE.txt
doc/html/MochiKit/Async.html
doc/html/MochiKit/Base.html
doc/html/MochiKit/Color.html
doc/html/MochiKit/DateTime.html
doc/html/MochiKit/DOM.html
doc/html/MochiKit/Format.html
doc/html/MochiKit/index.html
doc/html/MochiKit/Iter.html
doc/html/MochiKit/Logging.html
doc/html/MochiKit/LoggingPane.html
doc/html/MochiKit/Signal.html
doc/html/MochiKit/VersionHistory.html
doc/html/MochiKit/Visual.html
doc/js/toc.js
doc/rst/MochiKit/Async.rst
doc/rst/MochiKit/Base.rst
doc/rst/MochiKit/Color.rst
doc/rst/MochiKit/DateTime.rst
doc/rst/MochiKit/DOM.rst
doc/rst/MochiKit/Format.rst
doc/rst/MochiKit/index.rst
doc/rst/MochiKit/Iter.rst
doc/rst/MochiKit/Logging.rst
doc/rst/MochiKit/LoggingPane.rst
doc/rst/MochiKit/Signal.rst
doc/rst/MochiKit/VersionHistory.rst
doc/rst/MochiKit/Visual.rst
examples/ajax_tables/ajax_tables.css
examples/ajax_tables/ajax_tables.js
examples/ajax_tables/domains.json
examples/ajax_tables/domains.xml
examples/ajax_tables/index.html
examples/color_wheel/color_wheel.css
examples/color_wheel/color_wheel.js
examples/color_wheel/index.html
examples/draggable/draggable.css
examples/draggable/draggable.js
examples/draggable/index.html
examples/interpreter/index.html
examples/interpreter/interpreter.css
examples/interpreter/interpreter.js
examples/key_events/index.html
examples/key_events/key_events.css
examples/key_events/key_events.js
examples/logging_pane/index.html
examples/logging_pane/logging_pane.css
examples/logging_pane/logging_pane.js
examples/mochiregexp/index.html
examples/mochiregexp/mochiregexp.css
examples/mochiregexp/mochiregexp.js
examples/rounded_corners/index.html
examples/rounded_corners/rounded_corners.css
examples/rounded_corners/rounded_corners.js
examples/sortable_tables/index.html
examples/sortable_tables/sortable_tables.css
examples/sortable_tables/sortable_tables.js
examples/view-source/view-source.css
examples/view-source/view-source.html
examples/view-source/view-source.js
examples/view-source/lib/SyntaxHighlighter/shBrushCSharp.js
examples/view-source/lib/SyntaxHighlighter/shBrushDelphi.js
examples/view-source/lib/SyntaxHighlighter/shBrushJScript.js
examples/view-source/lib/SyntaxHighlighter/shBrushPhp.js
examples/view-source/lib/SyntaxHighlighter/shBrushPython.js
examples/view-source/lib/SyntaxHighlighter/shBrushSql.js
examples/view-source/lib/SyntaxHighlighter/shBrushVb.js
examples/view-source/lib/SyntaxHighlighter/shBrushXml.js
examples/view-source/lib/SyntaxHighlighter/shCore.js
examples/view-source/lib/SyntaxHighlighter/SyntaxHighlighter.css
examples/view-source/lib/SyntaxHighlighter/Tests.html
include/css/documentation.css
include/css/general.css
MochiKit/__package__.js
MochiKit/Async.js
MochiKit/Base.js
MochiKit/Color.js
MochiKit/DateTime.js
MochiKit/DOM.js
MochiKit/Format.js
MochiKit/Iter.js
MochiKit/Logging.js
MochiKit/LoggingPane.js
MochiKit/MochiKit.js
MochiKit/MockDOM.js
MochiKit/Signal.js
MochiKit/Test.js
MochiKit/Visual.js
packed/MochiKit/__package__.js
packed/MochiKit/MochiKit.js
tests/index.html
tests/test_Base.js
tests/test_Color.js
tests/test_DateTime.js
tests/test_Format.js
tests/test_Iter.js
tests/test_Logging.js
tests/test_MochiKit-Async.html
tests/test_MochiKit-Base.html
tests/test_MochiKit-Color.html
tests/test_MochiKit-DateTime.html
tests/test_MochiKit-DOM.html
tests/test_MochiKit-Format.html
tests/test_MochiKit-Iter.html
tests/test_MochiKit-JSAN.html
tests/test_MochiKit-Logging.html
tests/test_MochiKit-MochiKit.html
tests/test_MochiKit-Signal.html
tests/test_Signal.js
tests/SimpleTest/SimpleTest.js
tests/SimpleTest/test.css
tests/SimpleTest/TestRunner.js

View File

@@ -1,17 +0,0 @@
{
"recommends": {
"JSAN": "0.10"
},
"name": "MochiKit",
"license": "mit",
"author": [
"Bob Ippolito <bob@redivi.com>"
],
"abstract": "Python-inspired JavaScript kit",
"generated_by": "MochiKit's build script",
"build_requires": {
"Test.Simple": "0.11"
},
"version": "1.3.1",
"provides": {}
}

View File

@@ -1,606 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
<title>MochiKit.Async - manage asynchronous tasks</title>
<link rel="stylesheet" href="../../../include/css/documentation.css" type="text/css" />
<script type="text/javascript" src="../../../packed/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="../../js/toc.js"></script>
</head>
<body>
<div class="document">
<div class="section">
<h1><a id="name" name="name">Name</a></h1>
<p>MochiKit.Async - manage asynchronous tasks</p>
</div>
<div class="section">
<h1><a id="synopsis" name="synopsis">Synopsis</a></h1>
<pre class="literal-block">
var url = &quot;/src/b/bo/bob/MochiKit.Async/META.json&quot;;
/*
META.json looks something like this:
{&quot;name&quot;: &quot;MochiKit&quot;, &quot;version&quot;: &quot;0.5&quot;}
*/
var d = loadJSONDoc(url);
var gotMetadata = function (meta) {
if (MochiKit.Async.VERSION == meta.version) {
alert(&quot;You have the newest MochiKit.Async!&quot;);
} else {
alert(&quot;MochiKit.Async &quot;
+ meta.version
+ &quot; is available, upgrade!&quot;);
}
};
var metadataFetchFailed = function (err) {
alert(&quot;The metadata for MochiKit.Async could not be fetched :(&quot;);
};
d.addCallbacks(gotMetadata, metadataFetchFailed);
</pre>
</div>
<div class="section">
<h1><a id="description" name="description">Description</a></h1>
<p>MochiKit.Async provides facilities to manage asynchronous
(as in AJAX <a class="footnote-reference" href="#id7" id="id1" name="id1">[1]</a>) tasks. The model for asynchronous computation
used in this module is heavily inspired by Twisted <a class="footnote-reference" href="#id8" id="id2" name="id2">[2]</a>.</p>
</div>
<div class="section">
<h1><a id="dependencies" name="dependencies">Dependencies</a></h1>
<ul class="simple">
<li><a class="mochiref reference" href="Base.html">MochiKit.Base</a></li>
</ul>
</div>
<div class="section">
<h1><a id="overview" name="overview">Overview</a></h1>
<div class="section">
<h2><a id="deferred" name="deferred">Deferred</a></h2>
<p>The Deferred constructor encapsulates a single value that
is not available yet. The most important example of this
in the context of a web browser would be an <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>
to a server. The importance of the Deferred is that it
allows a consistent API to be exposed for all asynchronous
computations that occur exactly once.</p>
<p>The producer of the Deferred is responsible for doing all
of the complicated work behind the scenes. This often
means waiting for a timer to fire, or waiting for an event
(e.g. <tt class="docutils literal"><span class="pre">onreadystatechange</span></tt> of <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>).
It could also be coordinating several events (e.g.
<tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> with a timeout, or several Deferreds
(e.g. fetching a set of XML documents that should be
processed at the same time).</p>
<p>Since these sorts of tasks do not respond immediately, the
producer of the Deferred does the following steps before
returning to the consumer:</p>
<ol class="arabic simple">
<li>Create a <tt class="docutils literal"><span class="pre">new</span></tt> <a class="mochiref reference" href="#fn-deferred">Deferred();</a> object and keep a reference
to it, because it will be needed later when the value is
ready.</li>
<li>Setup the conditions to create the value requested (e.g.
create a new <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>, set its
<tt class="docutils literal"><span class="pre">onreadystatechange</span></tt>).</li>
<li>Return the <a class="mochiref reference" href="#fn-deferred">Deferred</a> object.</li>
</ol>
<p>Since the value is not yet ready, the consumer attaches
a function to the Deferred that will be called when the
value is ready. This is not unlike <tt class="docutils literal"><span class="pre">setTimeout</span></tt>, or
other similar facilities you may already be familiar with.
The consumer can also attach an &quot;errback&quot; to the
<a class="mochiref reference" href="#fn-deferred">Deferred</a>, which is a callback for error handling.</p>
<p>When the value is ready, the producer simply calls
<tt class="docutils literal"><span class="pre">myDeferred.callback(theValue)</span></tt>. If an error occurred,
it should call <tt class="docutils literal"><span class="pre">myDeferred.errback(theValue)</span></tt> instead.
As soon as this happens, the callback that the consumer
attached to the <a class="mochiref reference" href="#fn-deferred">Deferred</a> is called with <tt class="docutils literal"><span class="pre">theValue</span></tt>
as the only argument.</p>
<p>There are quite a few additional &quot;advanced&quot; features
baked into <a class="mochiref reference" href="#fn-deferred">Deferred</a>, such as cancellation and
callback chains, so take a look at the API
reference if you would like to know more!</p>
</div>
</div>
<div class="section">
<h1><a id="api-reference" name="api-reference">API Reference</a></h1>
<div class="section">
<h2><a id="errors" name="errors">Errors</a></h2>
<p>
<a name="fn-alreadycallederror"></a>
<a class="mochidef reference" href="#fn-alreadycallederror">AlreadyCalledError</a>:</p>
<blockquote>
Thrown by a <a class="mochiref reference" href="#fn-deferred">Deferred</a> if <tt class="docutils literal"><span class="pre">.callback</span></tt> or
<tt class="docutils literal"><span class="pre">.errback</span></tt> are called more than once.</blockquote>
<p>
<a name="fn-browsercomplianceerror"></a>
<a class="mochidef reference" href="#fn-browsercomplianceerror">BrowserComplianceError</a>:</p>
<blockquote>
Thrown when the JavaScript runtime is not capable of performing
the given function. Currently, this happens if the browser
does not support <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>.</blockquote>
<p>
<a name="fn-cancellederror"></a>
<a class="mochidef reference" href="#fn-cancellederror">CancelledError</a>:</p>
<blockquote>
Thrown by a <a class="mochiref reference" href="#fn-deferred">Deferred</a> when it is cancelled,
unless a canceller is present and throws something else.</blockquote>
<p>
<a name="fn-genericerror"></a>
<a class="mochidef reference" href="#fn-genericerror">GenericError</a>:</p>
<blockquote>
Results passed to <tt class="docutils literal"><span class="pre">.fail</span></tt> or <tt class="docutils literal"><span class="pre">.errback</span></tt> of a <a class="mochiref reference" href="#fn-deferred">Deferred</a>
are wrapped by this <tt class="docutils literal"><span class="pre">Error</span></tt> if <tt class="docutils literal"><span class="pre">!(result</span> <span class="pre">instanceof</span> <span class="pre">Error)</span></tt>.</blockquote>
<p>
<a name="fn-xmlhttprequesterror"></a>
<a class="mochidef reference" href="#fn-xmlhttprequesterror">XMLHttpRequestError</a>:</p>
<blockquote>
Thrown when an <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> does not complete successfully
for any reason. The <tt class="docutils literal"><span class="pre">req</span></tt> property of the error is the failed
<tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> object, and for convenience the <tt class="docutils literal"><span class="pre">number</span></tt>
property corresponds to <tt class="docutils literal"><span class="pre">req.status</span></tt>.</blockquote>
</div>
<div class="section">
<h2><a id="constructors" name="constructors">Constructors</a></h2>
<p>
<a name="fn-deferred"></a>
<a class="mochidef reference" href="#fn-deferred">Deferred()</a>:</p>
<blockquote>
Encapsulates a sequence of callbacks in response to a value that
may not yet be available. This is modeled after the Deferred class
from Twisted <a class="footnote-reference" href="#id9" id="id3" name="id3">[3]</a>.</blockquote>
<blockquote>
<p>Why do we want this? JavaScript has no threads, and even if it did,
threads are hard. Deferreds are a way of abstracting non-blocking
events, such as the final response to an <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>.</p>
<p>The sequence of callbacks is internally represented as a list
of 2-tuples containing the callback/errback pair. For example,
the following call sequence:</p>
<pre class="literal-block">
var d = new Deferred();
d.addCallback(myCallback);
d.addErrback(myErrback);
d.addBoth(myBoth);
d.addCallbacks(myCallback, myErrback);
</pre>
<p>is translated into a <a class="mochiref reference" href="#fn-deferred">Deferred</a> with the following internal
representation:</p>
<pre class="literal-block">
[
[myCallback, null],
[null, myErrback],
[myBoth, myBoth],
[myCallback, myErrback]
]
</pre>
<p>The <a class="mochiref reference" href="#fn-deferred">Deferred</a> also keeps track of its current status (fired).
Its status may be one of the following three values:</p>
<blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="14%" />
<col width="86%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Value</th>
<th class="head">Condition</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>-1</td>
<td>no value yet (initial condition)</td>
</tr>
<tr><td>0</td>
<td>success</td>
</tr>
<tr><td>1</td>
<td>error</td>
</tr>
</tbody>
</table>
</blockquote>
<p>A <a class="mochiref reference" href="#fn-deferred">Deferred</a> will be in the error state if one of the following
conditions are met:</p>
<ol class="arabic simple">
<li>The result given to callback or errback is &quot;<tt class="docutils literal"><span class="pre">instanceof</span> <span class="pre">Error</span></tt>&quot;</li>
<li>The callback or errback threw while executing. If the thrown object
is not <tt class="docutils literal"><span class="pre">instanceof</span> <span class="pre">Error</span></tt>, it will be wrapped with
<a class="mochiref reference" href="#fn-genericerror">GenericError</a>.</li>
</ol>
<p>Otherwise, the <a class="mochiref reference" href="#fn-deferred">Deferred</a> will be in the success state. The state
of the <a class="mochiref reference" href="#fn-deferred">Deferred</a> determines the next element in the callback
sequence to run.</p>
<p>When a callback or errback occurs with the example deferred chain, something
equivalent to the following will happen (imagine that exceptions are caught
and returned as-is):</p>
<pre class="literal-block">
// d.callback(result) or d.errback(result)
if (!(result instanceof Error)) {
result = myCallback(result);
}
if (result instanceof Error) {
result = myErrback(result);
}
result = myBoth(result);
if (result instanceof Error) {
result = myErrback(result);
} else {
result = myCallback(result);
}
</pre>
<p>The result is then stored away in case another step is added to the
callback sequence. Since the <a class="mochiref reference" href="#fn-deferred">Deferred</a> already has a value
available, any new callbacks added will be called immediately.</p>
<p>There are two other &quot;advanced&quot; details about this implementation that are
useful:</p>
<p>Callbacks are allowed to return <a class="mochiref reference" href="#fn-deferred">Deferred</a> instances,
so you can build complicated sequences of events with (relative) ease.</p>
<p>The creator of the <a class="mochiref reference" href="#fn-deferred">Deferred</a> may specify a canceller. The
canceller is a function that will be called if
<a class="mochiref reference" href="#fn-deferred.prototype.cancel">Deferred.prototype.cancel</a> is called before the
<a class="mochiref reference" href="#fn-deferred">Deferred</a> fires. You can use this to allow an
<tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> to be cleanly cancelled, for example. Note that
cancel will fire the <a class="mochiref reference" href="#fn-deferred">Deferred</a> with a
<a class="mochiref reference" href="#fn-cancellederror">CancelledError</a> (unless your canceller throws or returns
a different <tt class="docutils literal"><span class="pre">Error</span></tt>), so errbacks should be prepared to handle that
<tt class="docutils literal"><span class="pre">Error</span></tt> gracefully for cancellable <a class="mochiref reference" href="#fn-deferred">Deferred</a> instances.</p>
</blockquote>
<p>
<a name="fn-deferred.prototype.addboth"></a>
<a class="mochidef reference" href="#fn-deferred.prototype.addboth">Deferred.prototype.addBoth(func)</a>:</p>
<blockquote>
<p>Add the same function as both a callback and an errback as the
next element on the callback sequence. This is useful for code
that you want to guarantee to run, e.g. a finalizer.</p>
<p>If additional arguments are given, then <tt class="docutils literal"><span class="pre">func</span></tt> will be replaced
with <a class="mochiref reference" href="Base.html#fn-partial">MochiKit.Base.partial.apply(null, arguments)</a>. This
differs from <a class="reference" href="http://twistedmatrix.com/">Twisted</a>, because the result of the callback or
errback will be the <em>last</em> argument passed to <tt class="docutils literal"><span class="pre">func</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">func</span></tt> returns a <a class="mochiref reference" href="#fn-deferred">Deferred</a>, then it will be chained
(its value or error will be passed to the next callback). Note that
once the returned <tt class="docutils literal"><span class="pre">Deferred</span></tt> is chained, it can no longer accept new
callbacks.</p>
</blockquote>
<p>
<a name="fn-deferred.prototype.addcallback"></a>
<a class="mochidef reference" href="#fn-deferred.prototype.addcallback">Deferred.prototype.addCallback(func[, ...])</a>:</p>
<blockquote>
<p>Add a single callback to the end of the callback sequence.</p>
<p>If additional arguments are given, then <tt class="docutils literal"><span class="pre">func</span></tt> will be replaced
with <a class="mochiref reference" href="Base.html#fn-partial">MochiKit.Base.partial.apply(null, arguments)</a>. This
differs from <a class="reference" href="http://twistedmatrix.com/">Twisted</a>, because the result of the callback will
be the <em>last</em> argument passed to <tt class="docutils literal"><span class="pre">func</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">func</span></tt> returns a <a class="mochiref reference" href="#fn-deferred">Deferred</a>, then it will be chained
(its value or error will be passed to the next callback). Note that
once the returned <tt class="docutils literal"><span class="pre">Deferred</span></tt> is chained, it can no longer accept new
callbacks.</p>
</blockquote>
<p>
<a name="fn-deferred.prototype.addcallbacks"></a>
<a class="mochidef reference" href="#fn-deferred.prototype.addcallbacks">Deferred.prototype.addCallbacks(callback, errback)</a>:</p>
<blockquote>
<p>Add separate callback and errback to the end of the callback
sequence. Either callback or errback may be <tt class="docutils literal"><span class="pre">null</span></tt>,
but not both.</p>
<p>If <tt class="docutils literal"><span class="pre">callback</span></tt> or <tt class="docutils literal"><span class="pre">errback</span></tt> returns a <a class="mochiref reference" href="#fn-deferred">Deferred</a>,
then it will be chained (its value or error will be passed to the
next callback). Note that once the returned <tt class="docutils literal"><span class="pre">Deferred</span></tt> is chained,
it can no longer accept new callbacks.</p>
</blockquote>
<p>
<a name="fn-deferred.prototype.adderrback"></a>
<a class="mochidef reference" href="#fn-deferred.prototype.adderrback">Deferred.prototype.addErrback(func)</a>:</p>
<blockquote>
<p>Add a single errback to the end of the callback sequence.</p>
<p>If additional arguments are given, then <tt class="docutils literal"><span class="pre">func</span></tt> will be replaced
with <a class="mochiref reference" href="Base.html#fn-partial">MochiKit.Base.partial.apply(null, arguments)</a>. This
differs from <a class="reference" href="http://twistedmatrix.com/">Twisted</a>, because the result of the errback will
be the <em>last</em> argument passed to <tt class="docutils literal"><span class="pre">func</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">func</span></tt> returns a <a class="mochiref reference" href="#fn-deferred">Deferred</a>, then it will be chained
(its value or error will be passed to the next callback). Note that
once the returned <tt class="docutils literal"><span class="pre">Deferred</span></tt> is chained, it can no longer accept new
callbacks.</p>
</blockquote>
<p>
<a name="fn-deferred.prototype.callback"></a>
<a class="mochidef reference" href="#fn-deferred.prototype.callback">Deferred.prototype.callback([result])</a>:</p>
<blockquote>
<p>Begin the callback sequence with a non-<tt class="docutils literal"><span class="pre">Error</span></tt> result. Result
may be any value except for a <a class="mochiref reference" href="#fn-deferred">Deferred</a>.</p>
<p>Either <tt class="docutils literal"><span class="pre">.callback</span></tt> or <tt class="docutils literal"><span class="pre">.errback</span></tt> should
be called exactly once on a <a class="mochiref reference" href="#fn-deferred">Deferred</a>.</p>
</blockquote>
<p>
<a name="fn-deferred.prototype.cancel"></a>
<a class="mochidef reference" href="#fn-deferred.prototype.cancel">Deferred.prototype.cancel()</a>:</p>
<blockquote>
<p>Cancels a <a class="mochiref reference" href="#fn-deferred">Deferred</a> that has not yet received a value,
or is waiting on another <a class="mochiref reference" href="#fn-deferred">Deferred</a> as its value.</p>
<p>If a canceller is defined, the canceller is called.
If the canceller did not return an <tt class="docutils literal"><span class="pre">Error</span></tt>, or there
was no canceller, then the errback chain is started
with <a class="mochiref reference" href="#fn-cancellederror">CancelledError</a>.</p>
</blockquote>
<p>
<a name="fn-deferred.prototype.errback"></a>
<a class="mochidef reference" href="#fn-deferred.prototype.errback">Deferred.prototype.errback([result])</a>:</p>
<blockquote>
<p>Begin the callback sequence with an error result.
Result may be any value except for a <a class="mochiref reference" href="#fn-deferred">Deferred</a>,
but if <tt class="docutils literal"><span class="pre">!(result</span> <span class="pre">instanceof</span> <span class="pre">Error)</span></tt>, it will be wrapped
with <a class="mochiref reference" href="#fn-genericerror">GenericError</a>.</p>
<p>Either <tt class="docutils literal"><span class="pre">.callback</span></tt> or <tt class="docutils literal"><span class="pre">.errback</span></tt> should
be called exactly once on a
<a name="fn-deferred"></a>
<a class="mochidef reference" href="#fn-deferred">Deferred</a>.</p>
</blockquote>
<p>
<a name="fn-deferredlock"></a>
<a class="mochidef reference" href="#fn-deferredlock">DeferredLock()</a>:</p>
<blockquote>
<p>A lock for asynchronous systems.</p>
<p>The <tt class="docutils literal"><span class="pre">locked</span></tt> property of a <a class="mochiref reference" href="#fn-deferredlock">DeferredLock</a> will be <tt class="docutils literal"><span class="pre">true</span></tt> if
it locked, <tt class="docutils literal"><span class="pre">false</span></tt> otherwise. Do not change this property.</p>
</blockquote>
<p>
<a name="fn-deferredlock.prototype.acquire"></a>
<a class="mochidef reference" href="#fn-deferredlock.prototype.acquire">DeferredLock.prototype.acquire()</a>:</p>
<blockquote>
Attempt to acquire the lock. Returns a <a class="mochiref reference" href="#fn-deferred">Deferred</a> that fires on
lock acquisition with the <a class="mochiref reference" href="#fn-deferredlock">DeferredLock</a> as the value.
If the lock is locked, then the <a class="mochiref reference" href="#fn-deferred">Deferred</a> goes into a waiting
list.</blockquote>
<p>
<a name="fn-deferredlock.prototype.release"></a>
<a class="mochidef reference" href="#fn-deferredlock.prototype.release">DeferredLock.prototype.release()</a>:</p>
<blockquote>
Release the lock. If there is a waiting list, then the first
<a class="mochiref reference" href="#fn-deferred">Deferred</a> in that waiting list will be called back.</blockquote>
<p>
<a name="fn-deferredlist"></a>
<a class="mochidef reference" href="#fn-deferredlist">DeferredList(list, [fireOnOneCallback, fireOnOneErrback, consumeErrors, canceller])</a>:</p>
<blockquote>
<p>Combine a list of <a class="mochiref reference" href="#fn-deferred">Deferred</a> into one. Track the callbacks and
return a list of (success, result) tuples, 'success' being a boolean
indicating whether result is a normal result or an error.</p>
<p>Once created, you have access to all <a class="mochiref reference" href="#fn-deferred">Deferred</a> methods, like
addCallback, addErrback, addBoth. The behaviour can be changed by the
following options:</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">fireOnOneCallback</span></tt>:</dt>
<dd>Flag for launching the callback once the first Deferred of the list
has returned.</dd>
<dt><tt class="docutils literal"><span class="pre">fireOnOneErrback</span></tt>:</dt>
<dd>Flag for calling the errback at the first error of a Deferred.</dd>
<dt><tt class="docutils literal"><span class="pre">consumeErrors</span></tt>:</dt>
<dd>Flag indicating that any errors raised in the Deferreds should be
consumed by the DeferredList.</dd>
</dl>
<p>Example:</p>
<pre class="literal-block">
// We need to fetch data from 2 different urls
var d1 = loadJSONDoc(url1);
var d2 = loadJSONDoc(url2);
var l1 = new DeferredList([d1, d2], false, false, true);
l1.addCallback(function (resultList) {
MochiKit.Base.map(function (result) {
if (result[0]) {
alert(&quot;Data is here: &quot; + result[1]);
} else {
alert(&quot;Got an error: &quot; + result[1]);
}
}, resultList);
});
</pre>
</blockquote>
</div>
<div class="section">
<h2><a id="functions" name="functions">Functions</a></h2>
<p>
<a name="fn-calllater"></a>
<a class="mochidef reference" href="#fn-calllater">callLater(seconds, func[, args...])</a>:</p>
<blockquote>
<p>Call <tt class="docutils literal"><span class="pre">func(args...)</span></tt> after at least <tt class="docutils literal"><span class="pre">seconds</span></tt> seconds have elapsed.
This is a convenience method for:</p>
<pre class="literal-block">
func = partial.apply(extend(null, arguments, 1));
return wait(seconds).addCallback(function (res) { return func() });
</pre>
<p>Returns a cancellable <a class="mochiref reference" href="#fn-deferred">Deferred</a>.</p>
</blockquote>
<p>
<a name="fn-dosimplexmlhttprequest"></a>
<a class="mochidef reference" href="#fn-dosimplexmlhttprequest">doSimpleXMLHttpRequest(url[, queryArguments...])</a>:</p>
<blockquote>
<p>Perform a simple <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> and wrap it with a
<a class="mochiref reference" href="#fn-deferred">Deferred</a> that may be cancelled.</p>
<p>Note that currently, only <tt class="docutils literal"><span class="pre">200</span></tt> (OK) and <tt class="docutils literal"><span class="pre">304</span></tt>
(NOT_MODIFIED) are considered success codes at this time, other
status codes will result in an errback with an <tt class="docutils literal"><span class="pre">XMLHttpRequestError</span></tt>.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">url</span></tt>:</dt>
<dd>The URL to GET</dd>
<dt><tt class="docutils literal"><span class="pre">queryArguments</span></tt>:</dt>
<dd><p class="first">If this function is called with more than one argument, a <tt class="docutils literal"><span class="pre">&quot;?&quot;</span></tt>
and the result of <a class="mochiref reference" href="Base.html#fn-querystring">MochiKit.Base.queryString</a> with
the rest of the arguments are appended to the URL.</p>
<p>For example, this will do a GET request to the URL
<tt class="docutils literal"><span class="pre">http://example.com?bar=baz</span></tt>:</p>
<pre class="last literal-block">
doSimpleXMLHttpRequest(&quot;http://example.com&quot;, {bar: &quot;baz&quot;});
</pre>
</dd>
<dt><em>returns</em>:</dt>
<dd><a class="mochiref reference" href="#fn-deferred">Deferred</a> that will callback with the <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>
instance on success</dd>
</dl>
</blockquote>
<p>
<a name="fn-evaljsonrequest"></a>
<a class="mochidef reference" href="#fn-evaljsonrequest">evalJSONRequest(req)</a>:</p>
<blockquote>
<p>Evaluate a JSON <a class="footnote-reference" href="#id10" id="id4" name="id4">[4]</a> <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt></p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">req</span></tt>:</dt>
<dd>The request whose <tt class="docutils literal"><span class="pre">.responseText</span></tt> property is to be evaluated</dd>
<dt><em>returns</em>:</dt>
<dd>A JavaScript object</dd>
</dl>
</blockquote>
<p>
<a name="fn-fail"></a>
<a class="mochidef reference" href="#fn-fail">fail([result])</a>:</p>
<blockquote>
<p>Return a <a class="mochiref reference" href="#fn-deferred">Deferred</a> that has already had <tt class="docutils literal"><span class="pre">.errback(result)</span></tt>
called.</p>
<p>See <tt class="docutils literal"><span class="pre">succeed</span></tt> documentation for rationale.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">result</span></tt>:</dt>
<dd>The result to give to <a class="mochiref reference" href="#fn-deferred.prototype.errback">Deferred.prototype.errback(result)</a>.</dd>
<dt><em>returns</em>:</dt>
<dd>A <tt class="docutils literal"><span class="pre">new</span></tt> <a class="mochiref reference" href="#fn-deferred">Deferred()</a></dd>
</dl>
</blockquote>
<p>
<a name="fn-gatherresults"></a>
<a class="mochidef reference" href="#fn-gatherresults">gatherResults(deferreds)</a>:</p>
<blockquote>
A convenience function that returns a <a class="mochiref reference" href="#fn-deferredlist">DeferredList</a>
from the given <tt class="docutils literal"><span class="pre">Array</span></tt> of <a class="mochiref reference" href="#fn-deferred">Deferred</a> instances
that will callback with an <tt class="docutils literal"><span class="pre">Array</span></tt> of just results when
they're available, or errback on the first array.</blockquote>
<p>
<a name="fn-getxmlhttprequest"></a>
<a class="mochidef reference" href="#fn-getxmlhttprequest">getXMLHttpRequest()</a>:</p>
<blockquote>
<p>Return an <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> compliant object for the current
platform.</p>
<p>In order of preference:</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">new</span> <span class="pre">XMLHttpRequest()</span></tt></li>
<li><tt class="docutils literal"><span class="pre">new</span> <span class="pre">ActiveXObject('Msxml2.XMLHTTP')</span></tt></li>
<li><tt class="docutils literal"><span class="pre">new</span> <span class="pre">ActiveXObject('Microsoft.XMLHTTP')</span></tt></li>
<li><tt class="docutils literal"><span class="pre">new</span> <span class="pre">ActiveXObject('Msxml2.XMLHTTP.4.0')</span></tt></li>
</ul>
</blockquote>
<p>
<a name="fn-maybedeferred"></a>
<a class="mochidef reference" href="#fn-maybedeferred">maybeDeferred(func[, argument...])</a>:</p>
<blockquote>
<p>Call a <tt class="docutils literal"><span class="pre">func</span></tt> with the given arguments and ensure the result is a
<a class="mochiref reference" href="#fn-deferred">Deferred</a>.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">func</span></tt>:</dt>
<dd>The function to call.</dd>
<dt><em>returns</em>:</dt>
<dd>A new <a class="mochiref reference" href="#fn-deferred">Deferred</a> based on the call to <tt class="docutils literal"><span class="pre">func</span></tt>. If <tt class="docutils literal"><span class="pre">func</span></tt>
does not naturally return a <a class="mochiref reference" href="#fn-deferred">Deferred</a>, its result or error
value will be wrapped by one.</dd>
</dl>
</blockquote>
<p>
<a name="fn-loadjsondoc"></a>
<a class="mochidef reference" href="#fn-loadjsondoc">loadJSONDoc(url)</a>:</p>
<blockquote>
<p>Do a simple <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> to a URL and get the response
as a JSON <a class="footnote-reference" href="#id10" id="id5" name="id5">[4]</a> document.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">url</span></tt>:</dt>
<dd>The URL to GET</dd>
<dt><em>returns</em>:</dt>
<dd><a class="mochiref reference" href="#fn-deferred">Deferred</a> that will callback with the evaluated JSON <a class="footnote-reference" href="#id10" id="id6" name="id6">[4]</a>
response upon successful <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt></dd>
</dl>
</blockquote>
<p>
<a name="fn-sendxmlhttprequest"></a>
<a class="mochidef reference" href="#fn-sendxmlhttprequest">sendXMLHttpRequest(req[, sendContent])</a>:</p>
<blockquote>
<p>Set an <tt class="docutils literal"><span class="pre">onreadystatechange</span></tt> handler on an <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> object
and send it off. Will return a cancellable <a class="mochiref reference" href="#fn-deferred">Deferred</a> that will
callback on success.</p>
<p>Note that currently, only <tt class="docutils literal"><span class="pre">200</span></tt> (OK) and <tt class="docutils literal"><span class="pre">304</span></tt>
(NOT_MODIFIED) are considered success codes at this time, other
status codes will result in an errback with an <tt class="docutils literal"><span class="pre">XMLHttpRequestError</span></tt>.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">req</span></tt>:</dt>
<dd>An preconfigured <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt> object (open has been called).</dd>
<dt><tt class="docutils literal"><span class="pre">sendContent</span></tt>:</dt>
<dd>Optional string or DOM content to send over the <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>.</dd>
<dt><em>returns</em>:</dt>
<dd><a class="mochiref reference" href="#fn-deferred">Deferred</a> that will callback with the <tt class="docutils literal"><span class="pre">XMLHttpRequest</span></tt>
instance on success.</dd>
</dl>
</blockquote>
<p>
<a name="fn-succeed"></a>
<a class="mochidef reference" href="#fn-succeed">succeed([result])</a>:</p>
<blockquote>
<p>Return a <a class="mochiref reference" href="#fn-deferred">Deferred</a> that has already had <tt class="docutils literal"><span class="pre">.callback(result)</span></tt>
called.</p>
<p>This is useful when you're writing synchronous code to an asynchronous
interface: i.e., some code is calling you expecting a <a class="mochiref reference" href="#fn-deferred">Deferred</a>
result, but you don't actually need to do anything asynchronous. Just
return <tt class="docutils literal"><span class="pre">succeed(theResult)</span></tt>.</p>
<p>See <tt class="docutils literal"><span class="pre">fail</span></tt> for a version of this function that uses a failing
<a class="mochiref reference" href="#fn-deferred">Deferred</a> rather than a successful one.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">result</span></tt>:</dt>
<dd>The result to give to <a class="mochiref reference" href="#fn-deferred.prototype.callback">Deferred.prototype.callback(result)</a></dd>
<dt><em>returns</em>:</dt>
<dd>a <tt class="docutils literal"><span class="pre">new</span></tt> <a class="mochiref reference" href="#fn-deferred">Deferred</a></dd>
</dl>
</blockquote>
<p>
<a name="fn-wait"></a>
<a class="mochidef reference" href="#fn-wait">wait(seconds[, res])</a>:</p>
<blockquote>
Return a new cancellable <a class="mochiref reference" href="#fn-deferred">Deferred</a> that will <tt class="docutils literal"><span class="pre">.callback(res)</span></tt>
after at least <tt class="docutils literal"><span class="pre">seconds</span></tt> seconds have elapsed.</blockquote>
</div>
</div>
<div class="section">
<h1><a id="see-also" name="see-also">See Also</a></h1>
<table class="docutils footnote" frame="void" id="id7" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1" name="id7">[1]</a></td><td>AJAX, Asynchronous JavaScript and XML: <a class="reference" href="http://en.wikipedia.org/wiki/AJAX">http://en.wikipedia.org/wiki/AJAX</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id8" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id2" name="id8">[2]</a></td><td>Twisted, an event-driven networking framework written in Python: <a class="reference" href="http://twistedmatrix.com/">http://twistedmatrix.com/</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id9" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id3" name="id9">[3]</a></td><td>Twisted Deferred Reference: <a class="reference" href="http://twistedmatrix.com/projects/core/documentation/howto/defer.html">http://twistedmatrix.com/projects/core/documentation/howto/defer.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id10" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a name="id10">[4]</a></td><td><em>(<a class="fn-backref" href="#id4">1</a>, <a class="fn-backref" href="#id5">2</a>, <a class="fn-backref" href="#id6">3</a>)</em> JSON, JavaScript Object Notation: <a class="reference" href="http://json.org/">http://json.org/</a></td></tr>
</tbody>
</table>
</div>
<div class="section">
<h1><a id="authors" name="authors">Authors</a></h1>
<ul class="simple">
<li>Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;</li>
</ul>
</div>
<div class="section">
<h1><a id="copyright" name="copyright">Copyright</a></h1>
<p>Copyright 2005 Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;. This program is dual-licensed
free software; you can redistribute it and/or modify it under the terms of the
<a class="reference" href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or the <a class="reference" href="http://www.opensource.org/licenses/afl-2.1.php">Academic Free License v2.1</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -1,509 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
<title>MochiKit.Color - color abstraction with CSS3 support</title>
<link rel="stylesheet" href="../../../include/css/documentation.css" type="text/css" />
<script type="text/javascript" src="../../../packed/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="../../js/toc.js"></script>
</head>
<body>
<div class="document">
<div class="section">
<h1><a id="name" name="name">Name</a></h1>
<p>MochiKit.Color - color abstraction with CSS3 support</p>
</div>
<div class="section">
<h1><a id="synopsis" name="synopsis">Synopsis</a></h1>
<pre class="literal-block">
// RGB color expressions are supported
assert(
objEqual(Color.whiteColor(), Color.fromString(&quot;rgb(255,100%, 255)&quot;))
);
// So is instantiating directly from HSL or RGB values.
// Note that fromRGB and fromHSL take numbers between 0.0 and 1.0!
assert( objEqual(Color.fromRGB(1.0, 1.0, 1.0), Color.fromHSL(0.0, 0.0, 1.0) );
// Or even SVG color keyword names, as per CSS3!
assert( Color.fromString(&quot;aquamarine&quot;), &quot;#7fffd4&quot; );
// NSColor-like colors built in
assert( Color.whiteColor().toHexString() == &quot;#ffffff&quot; );
</pre>
</div>
<div class="section">
<h1><a id="description" name="description">Description</a></h1>
<p>MochiKit.Color is an abstraction for handling colors and strings that
represent colors.</p>
</div>
<div class="section">
<h1><a id="dependencies" name="dependencies">Dependencies</a></h1>
<ul class="simple">
<li><a class="mochiref reference" href="Base.html">MochiKit.Base</a></li>
</ul>
</div>
<div class="section">
<h1><a id="overview" name="overview">Overview</a></h1>
<p>MochiKit.Color provides an abstraction of RGB, HSL and HSV colors with alpha.
It supports parsing and generating of CSS3 colors, and has a full CSS3 (SVG)
color table.</p>
<p>All of the functionality in this module is exposed through a Color constructor
and its prototype, but a few of its internals are available for direct use at
module level.</p>
</div>
<div class="section">
<h1><a id="api-reference" name="api-reference">API Reference</a></h1>
<div class="section">
<h2><a id="constructors" name="constructors">Constructors</a></h2>
<p>
<a name="fn-color"></a>
<a class="mochidef reference" href="#fn-color">Color()</a>:</p>
<blockquote>
<p>Represents a color. Component values should be integers between <tt class="docutils literal"><span class="pre">0.0</span></tt>
and <tt class="docutils literal"><span class="pre">1.0</span></tt>. You should use one of the <a class="mochiref reference" href="#fn-color">Color</a> factory
functions such as <a class="mochiref reference" href="#fn-color.fromrgb">Color.fromRGB</a>, <a class="mochiref reference" href="#fn-color.fromhsl">Color.fromHSL</a>,
etc. instead of constructing <a class="mochiref reference" href="#fn-color">Color</a> objects directly.</p>
<p><a class="mochiref reference" href="#fn-color">Color</a> instances can be compared with
<a class="mochiref reference" href="Base.html#fn-compare">MochiKit.Base.compare</a> (though ordering is on RGB, so is not
particularly meaningful except for equality), and the default <tt class="docutils literal"><span class="pre">toString</span></tt>
implementation returns <a class="mochiref reference" href="#fn-color.prototype.tohexstring">Color.prototype.toHexString()</a>.</p>
<p><a class="mochiref reference" href="#fn-color">Color</a> instances are immutable, and much of the architecture is
inspired by AppKit's NSColor <a class="footnote-reference" href="#id6" id="id1" name="id1">[1]</a></p>
</blockquote>
<p>
<a name="fn-color.frombackground"></a>
<a class="mochidef reference" href="#fn-color.frombackground">Color.fromBackground(elem)</a>:</p>
<blockquote>
<p>Returns a <a class="mochiref reference" href="#fn-color">Color</a> object based on the background of the provided
element. Equivalent to:</p>
<pre class="literal-block">
c = Color.fromComputedStyle(
elem, &quot;backgroundColor&quot;, &quot;background-color&quot;) || Color.whiteColor();
</pre>
</blockquote>
<p>
<a name="fn-color.fromcomputedstyle"></a>
<a class="mochidef reference" href="#fn-color.fromcomputedstyle">Color.fromComputedStyle(elem, style, mozillaEquivalentCSS)</a>:</p>
<blockquote>
Returns a <a class="mochiref reference" href="#fn-color">Color</a> object based on the result of
<a class="mochiref reference" href="DOM.html#fn-computedstyle">MochiKit.DOM.computedStyle(elem, style, mozillaEquivalentCSS)</a>
or <tt class="docutils literal"><span class="pre">null</span></tt> if not found.</blockquote>
<p>
<a name="fn-color.fromhexstring"></a>
<a class="mochidef reference" href="#fn-color.fromhexstring">Color.fromHexString(hexString)</a>:</p>
<blockquote>
Returns a <a class="mochiref reference" href="#fn-color">Color</a> object from the given hexadecimal color string.
For example, <tt class="docutils literal"><span class="pre">&quot;#FFFFFF&quot;</span></tt> would return a <a class="mochiref reference" href="#fn-color">Color</a> with
RGB values <tt class="docutils literal"><span class="pre">[255/255,</span> <span class="pre">255/255,</span> <span class="pre">255/255]</span></tt> (white).</blockquote>
<p>
<a name="fn-color.fromhsl"></a>
<a class="mochidef reference" href="#fn-color.fromhsl">Color.fromHSL(hue, saturation, lightness, alpha=1.0)</a>:</p>
<blockquote>
<p>Return a <a class="mochiref reference" href="#fn-color">Color</a> object from the given <tt class="docutils literal"><span class="pre">hue</span></tt>, <tt class="docutils literal"><span class="pre">saturation</span></tt>,
<tt class="docutils literal"><span class="pre">lightness</span></tt> values. Values should be numbers between <tt class="docutils literal"><span class="pre">0.0</span></tt> and
<tt class="docutils literal"><span class="pre">1.0</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">alpha</span></tt> is not given, then <tt class="docutils literal"><span class="pre">1.0</span></tt> (completely opaque) will be used.</p>
<dl class="docutils">
<dt>Alternate form:</dt>
<dd><a class="mochiref reference" href="#fn-color.fromhsl">Color.fromHSL({h: hue, s: saturation, l: lightness, a: alpha})</a></dd>
</dl>
</blockquote>
<p>
<a name="fn-color.fromhslstring"></a>
<a class="mochidef reference" href="#fn-color.fromhslstring">Color.fromHSLString(hslString)</a>:</p>
<blockquote>
Returns a <a class="mochiref reference" href="#fn-color">Color</a> object from the given decimal hsl color string.
For example, <tt class="docutils literal"><span class="pre">&quot;hsl(0,0%,100%)&quot;</span></tt> would return a <a class="mochiref reference" href="#fn-color">Color</a> with
HSL values <tt class="docutils literal"><span class="pre">[0/360,</span> <span class="pre">0/360,</span> <span class="pre">360/360]</span></tt> (white).</blockquote>
<p>
<a name="fn-color.fromhsv"></a>
<a class="mochidef reference" href="#fn-color.fromhsv">Color.fromHSV(hue, saturation, value, alpha=1.0)</a>:</p>
<blockquote>
<p>Return a <a class="mochiref reference" href="#fn-color">Color</a> object from the given <tt class="docutils literal"><span class="pre">hue</span></tt>, <tt class="docutils literal"><span class="pre">saturation</span></tt>,
<tt class="docutils literal"><span class="pre">value</span></tt> values. Values should be numbers between <tt class="docutils literal"><span class="pre">0.0</span></tt> and
<tt class="docutils literal"><span class="pre">1.0</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">alpha</span></tt> is not given, then <tt class="docutils literal"><span class="pre">1.0</span></tt> (completely opaque) will be used.</p>
<dl class="docutils">
<dt>Alternate form:</dt>
<dd><a class="mochiref reference" href="#fn-color.fromhsv">Color.fromHSV({h: hue, s: saturation, v: value, a: alpha})</a></dd>
</dl>
</blockquote>
<p>
<a name="fn-color.fromname"></a>
<a class="mochidef reference" href="#fn-color.fromname">Color.fromName(colorName)</a>:</p>
<blockquote>
Returns a <a class="mochiref reference" href="#fn-color">Color</a> object corresponding to the given
SVG 1.0 color keyword name <a class="footnote-reference" href="#id7" id="id2" name="id2">[2]</a> as per the W3C CSS3
Color Module <a class="footnote-reference" href="#id8" id="id3" name="id3">[3]</a>. <tt class="docutils literal"><span class="pre">&quot;transparent&quot;</span></tt> is also accepted
as a color name, and will return <a class="mochiref reference" href="#fn-color.transparentcolor">Color.transparentColor()</a>.</blockquote>
<p>
<a name="fn-color.fromrgb"></a>
<a class="mochidef reference" href="#fn-color.fromrgb">Color.fromRGB(red, green, blue, alpha=1.0)</a>:</p>
<blockquote>
<p>Return a <a class="mochiref reference" href="#fn-color">Color</a> object from the given <tt class="docutils literal"><span class="pre">red</span></tt>, <tt class="docutils literal"><span class="pre">green</span></tt>,
<tt class="docutils literal"><span class="pre">blue</span></tt>, and <tt class="docutils literal"><span class="pre">alpha</span></tt> values. Values should be numbers between <tt class="docutils literal"><span class="pre">0</span></tt>
and <tt class="docutils literal"><span class="pre">1.0</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">alpha</span></tt> is not given, then <tt class="docutils literal"><span class="pre">1.0</span></tt> (completely opaque) will be used.</p>
<dl class="docutils">
<dt>Alternate form:</dt>
<dd><a class="mochiref reference" href="#fn-color.fromrgb">Color.fromRGB({r: red, g: green, b: blue, a: alpha})</a></dd>
</dl>
</blockquote>
<p>
<a name="fn-color.fromrgbstring"></a>
<a class="mochidef reference" href="#fn-color.fromrgbstring">Color.fromRGBString(rgbString)</a>:</p>
<blockquote>
Returns a <a class="mochiref reference" href="#fn-color">Color</a> object from the given decimal rgb color string.
For example, <tt class="docutils literal"><span class="pre">&quot;rgb(255,255,255)&quot;</span></tt> would return a <a class="mochiref reference" href="#fn-color">Color</a> with
RGB values <tt class="docutils literal"><span class="pre">[255/255,</span> <span class="pre">255/255,</span> <span class="pre">255/255]</span></tt> (white).</blockquote>
<p>
<a name="fn-color.fromtext"></a>
<a class="mochidef reference" href="#fn-color.fromtext">Color.fromText(elem)</a>:</p>
<blockquote>
<p>Returns a <a class="mochiref reference" href="#fn-color">Color</a> object based on the text color of the provided
element. Equivalent to:</p>
<pre class="literal-block">
c = Color.fromComputedStyle(elem, &quot;color&quot;) || Color.whiteColor();
</pre>
</blockquote>
<p>
<a name="fn-color.fromstring"></a>
<a class="mochidef reference" href="#fn-color.fromstring">Color.fromString(rgbOrHexString)</a>:</p>
<blockquote>
<p>Returns a <a class="mochiref reference" href="#fn-color">Color</a> object from the given RGB, HSL, hex, or name.
Will return <tt class="docutils literal"><span class="pre">null</span></tt> if the string can not be parsed by any of these
methods.</p>
<p>See <a class="mochiref reference" href="#fn-color.fromhexstring">Color.fromHexString</a>, <a class="mochiref reference" href="#fn-color.fromrgbstring">Color.fromRGBString</a>,
<a class="mochiref reference" href="#fn-color.fromhslstring">Color.fromHSLString</a> and <a class="mochiref reference" href="#fn-color.fromname">Color.fromName</a> more
information.</p>
</blockquote>
<p>
<a name="fn-color.namedcolors"></a>
<a class="mochidef reference" href="#fn-color.namedcolors">Color.namedColors()</a>:</p>
<blockquote>
Returns an object with properties for each SVG 1.0 color keyword
name <a class="footnote-reference" href="#id7" id="id4" name="id4">[2]</a> supported by CSS3 <a class="footnote-reference" href="#id8" id="id5" name="id5">[3]</a>. Property names are the color keyword
name in lowercase, and the value is a string suitable for
<a class="mochiref reference" href="#fn-color.fromstring">Color.fromString()</a>.</blockquote>
<p>
<a name="fn-color.prototype.colorwithalpha"></a>
<a class="mochidef reference" href="#fn-color.prototype.colorwithalpha">Color.prototype.colorWithAlpha(alpha)</a>:</p>
<blockquote>
Return a new <a class="mochiref reference" href="#fn-color">Color</a> based on this color, but with the provided
<tt class="docutils literal"><span class="pre">alpha</span></tt> value.</blockquote>
<p>
<a name="fn-color.prototype.colorwithhue"></a>
<a class="mochidef reference" href="#fn-color.prototype.colorwithhue">Color.prototype.colorWithHue(hue)</a>:</p>
<blockquote>
Return a new <a class="mochiref reference" href="#fn-color">Color</a> based on this color, but with the provided
<tt class="docutils literal"><span class="pre">hue</span></tt> value.</blockquote>
<p>
<a name="fn-color.prototype.colorwithsaturation"></a>
<a class="mochidef reference" href="#fn-color.prototype.colorwithsaturation">Color.prototype.colorWithSaturation(saturation)</a>:</p>
<blockquote>
Return a new <a class="mochiref reference" href="#fn-color">Color</a> based on this color, but with the provided
<tt class="docutils literal"><span class="pre">saturation</span></tt> value (using the HSL color model).</blockquote>
<p>
<a name="fn-color.prototype.colorwithlightness"></a>
<a class="mochidef reference" href="#fn-color.prototype.colorwithlightness">Color.prototype.colorWithLightness(lightness)</a>:</p>
<blockquote>
Return a new <a class="mochiref reference" href="#fn-color">Color</a> based on this color, but with the provided
<tt class="docutils literal"><span class="pre">lightness</span></tt> value.</blockquote>
<p>
<a name="fn-color.prototype.darkercolorwithlevel"></a>
<a class="mochidef reference" href="#fn-color.prototype.darkercolorwithlevel">Color.prototype.darkerColorWithLevel(level)</a>:</p>
<blockquote>
Return a new <a class="mochiref reference" href="#fn-color">Color</a> based on this color, but darker by the given
<tt class="docutils literal"><span class="pre">level</span></tt> (between <tt class="docutils literal"><span class="pre">0</span></tt> and <tt class="docutils literal"><span class="pre">1.0</span></tt>).</blockquote>
<p>
<a name="fn-color.prototype.lightercolorwithlevel"></a>
<a class="mochidef reference" href="#fn-color.prototype.lightercolorwithlevel">Color.prototype.lighterColorWithLevel(level)</a>:</p>
<blockquote>
Return a new <a class="mochiref reference" href="#fn-color">Color</a> based on this color, but lighter by the given
<tt class="docutils literal"><span class="pre">level</span></tt> (between <tt class="docutils literal"><span class="pre">0</span></tt> and <tt class="docutils literal"><span class="pre">1.0</span></tt>).</blockquote>
<p>
<a name="fn-color.prototype.blendedcolor"></a>
<a class="mochidef reference" href="#fn-color.prototype.blendedcolor">Color.prototype.blendedColor(other, fraction=0.5)</a>:</p>
<blockquote>
Return a new <a class="mochiref reference" href="#fn-color">Color</a> whose RGBA component values are a weighted sum
of this color and <tt class="docutils literal"><span class="pre">other</span></tt>. Each component of the returned color
is the <tt class="docutils literal"><span class="pre">fraction</span></tt> of other's value plus <tt class="docutils literal"><span class="pre">1</span> <span class="pre">-</span> <span class="pre">fraction</span></tt> of this
color's.</blockquote>
<p>
<a name="fn-color.prototype.islight"></a>
<a class="mochidef reference" href="#fn-color.prototype.islight">Color.prototype.isLight()</a>:</p>
<blockquote>
<p>Return <tt class="docutils literal"><span class="pre">true</span></tt> if the lightness value of this color is greater than
<tt class="docutils literal"><span class="pre">0.5</span></tt>.</p>
<p>Note that <tt class="docutils literal"><span class="pre">alpha</span></tt> is ignored for this calculation (color components
are not premultiplied).</p>
</blockquote>
<p>
<a name="fn-color.prototype.isdark"></a>
<a class="mochidef reference" href="#fn-color.prototype.isdark">Color.prototype.isDark()</a>:</p>
<blockquote>
<p>Return <tt class="docutils literal"><span class="pre">true</span></tt> if the lightness value of this color is less than or
equal to <tt class="docutils literal"><span class="pre">0.5</span></tt>.</p>
<p>Note that <tt class="docutils literal"><span class="pre">alpha</span></tt> is ignored for this calculation (color components
are not premultiplied).</p>
</blockquote>
<p>
<a name="fn-color.prototype.torgbstring"></a>
<a class="mochidef reference" href="#fn-color.prototype.torgbstring">Color.prototype.toRGBString()</a>:</p>
<blockquote>
<p>Return the decimal <tt class="docutils literal"><span class="pre">&quot;rgb(red,</span> <span class="pre">green,</span> <span class="pre">blue)&quot;</span></tt> string representation of this
color.</p>
<p>If the alpha component is not <tt class="docutils literal"><span class="pre">1.0</span></tt> (fully opaque), the
<tt class="docutils literal"><span class="pre">&quot;rgba(red,</span> <span class="pre">green,</span> <span class="pre">blue,</span> <span class="pre">alpha)&quot;</span></tt> string representation will be used.</p>
<p>For example:</p>
<pre class="literal-block">
assert( Color.whiteColor().toRGBString() == &quot;rgb(255,255,255)&quot; );
</pre>
</blockquote>
<p>
<a name="fn-color.prototype.tohslstring"></a>
<a class="mochidef reference" href="#fn-color.prototype.tohslstring">Color.prototype.toHSLString()</a>:</p>
<blockquote>
<p>Return the decimal <tt class="docutils literal"><span class="pre">&quot;hsl(hue,</span> <span class="pre">saturation,</span> <span class="pre">lightness)&quot;</span></tt>
string representation of this color.</p>
<p>If the alpha component is not <tt class="docutils literal"><span class="pre">1.0</span></tt> (fully opaque), the
<tt class="docutils literal"><span class="pre">&quot;hsla(hue,</span> <span class="pre">saturation,</span> <span class="pre">lightness,</span> <span class="pre">alpha)&quot;</span></tt> string representation
will be used.</p>
<p>For example:</p>
<pre class="literal-block">
assert( Color.whiteColor().toHSLString() == &quot;hsl(0,0,360)&quot; );
</pre>
</blockquote>
<p>
<a name="fn-color.prototype.tohexstring"></a>
<a class="mochidef reference" href="#fn-color.prototype.tohexstring">Color.prototype.toHexString()</a>:</p>
<blockquote>
<p>Return the hexadecimal <tt class="docutils literal"><span class="pre">&quot;#RRGGBB&quot;</span></tt> string representation of this color.</p>
<p>Note that the alpha component is completely ignored for hexadecimal
string representations!</p>
<p>For example:</p>
<pre class="literal-block">
assert( Color.whiteColor().toHexString() == &quot;#FFFFFF&quot; );
</pre>
</blockquote>
<p>
<a name="fn-color.prototype.asrgb"></a>
<a class="mochidef reference" href="#fn-color.prototype.asrgb">Color.prototype.asRGB()</a>:</p>
<blockquote>
Return the RGB (red, green, blue, alpha) components of this color as an
object with <tt class="docutils literal"><span class="pre">r</span></tt>, <tt class="docutils literal"><span class="pre">g</span></tt>, <tt class="docutils literal"><span class="pre">b</span></tt>, and <tt class="docutils literal"><span class="pre">a</span></tt> properties that have
values between <tt class="docutils literal"><span class="pre">0.0</span></tt> and <tt class="docutils literal"><span class="pre">1.0</span></tt>.</blockquote>
<p>
<a name="fn-color.prototype.ashsl"></a>
<a class="mochidef reference" href="#fn-color.prototype.ashsl">Color.prototype.asHSL()</a>:</p>
<blockquote>
Return the HSL (hue, saturation, lightness, alpha) components of this
color as an object with <tt class="docutils literal"><span class="pre">h</span></tt>, <tt class="docutils literal"><span class="pre">s</span></tt>, <tt class="docutils literal"><span class="pre">l</span></tt> and <tt class="docutils literal"><span class="pre">a</span></tt> properties
that have values between <tt class="docutils literal"><span class="pre">0.0</span></tt> and <tt class="docutils literal"><span class="pre">1.0</span></tt>.</blockquote>
<p>
<a name="fn-color.prototype.ashsv"></a>
<a class="mochidef reference" href="#fn-color.prototype.ashsv">Color.prototype.asHSV()</a>:</p>
<blockquote>
Return the HSV (hue, saturation, value, alpha) components of this
color as an object with <tt class="docutils literal"><span class="pre">h</span></tt>, <tt class="docutils literal"><span class="pre">s</span></tt>, <tt class="docutils literal"><span class="pre">v</span></tt> and <tt class="docutils literal"><span class="pre">a</span></tt> properties
that have values between <tt class="docutils literal"><span class="pre">0.0</span></tt> and <tt class="docutils literal"><span class="pre">1.0</span></tt>.</blockquote>
<p>
<a name="fn-color.blackcolor"></a>
<a class="mochidef reference" href="#fn-color.blackcolor">Color.blackColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 0, 0, 0
(#000000).</blockquote>
<p>
<a name="fn-color.bluecolor"></a>
<a class="mochidef reference" href="#fn-color.bluecolor">Color.blueColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 0, 0, 1
(#0000ff).</blockquote>
<p>
<a name="fn-color.browncolor"></a>
<a class="mochidef reference" href="#fn-color.browncolor">Color.brownColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 0.6, 0.4, 0.2
(#996633).</blockquote>
<p>
<a name="fn-color.cyancolor"></a>
<a class="mochidef reference" href="#fn-color.cyancolor">Color.cyanColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 0, 1, 1
(#00ffff).</blockquote>
<p>
<a name="fn-color.darkgraycolor"></a>
<a class="mochidef reference" href="#fn-color.darkgraycolor">Color.darkGrayColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 1/3, 1/3, 1/3
(#555555).</blockquote>
<p>
<a name="fn-color.graycolor"></a>
<a class="mochidef reference" href="#fn-color.graycolor">Color.grayColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 0.5, 0.5, 0.5
(#808080).</blockquote>
<p>
<a name="fn-color.greencolor"></a>
<a class="mochidef reference" href="#fn-color.greencolor">Color.greenColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 0, 1, 0.
(#00ff00).</blockquote>
<p>
<a name="fn-color.lightgraycolor"></a>
<a class="mochidef reference" href="#fn-color.lightgraycolor">Color.lightGrayColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 2/3, 2/3, 2/3
(#aaaaaa).</blockquote>
<p>
<a name="fn-color.magentacolor"></a>
<a class="mochidef reference" href="#fn-color.magentacolor">Color.magentaColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 1, 0, 1
(#ff00ff).</blockquote>
<p>
<a name="fn-color.orangecolor"></a>
<a class="mochidef reference" href="#fn-color.orangecolor">Color.orangeColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 1, 0.5, 0
(#ff8000).</blockquote>
<p>
<a name="fn-color.purplecolor"></a>
<a class="mochidef reference" href="#fn-color.purplecolor">Color.purpleColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 0.5, 0, 0.5
(#800080).</blockquote>
<p>
<a name="fn-color.redcolor"></a>
<a class="mochidef reference" href="#fn-color.redcolor">Color.redColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 1, 0, 0
(#ff0000).</blockquote>
<p>
<a name="fn-color.whitecolor"></a>
<a class="mochidef reference" href="#fn-color.whitecolor">Color.whiteColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 1, 1, 1
(#ffffff).</blockquote>
<p>
<a name="fn-color.yellowcolor"></a>
<a class="mochidef reference" href="#fn-color.yellowcolor">Color.yellowColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object whose RGB values are 1, 1, 0
(#ffff00).</blockquote>
<p>
<a name="fn-color.transparentcolor"></a>
<a class="mochidef reference" href="#fn-color.transparentcolor">Color.transparentColor()</a>:</p>
<blockquote>
Return a <a class="mochiref reference" href="#fn-color">Color</a> object that is completely transparent
(has alpha component of 0).</blockquote>
</div>
<div class="section">
<h2><a id="functions" name="functions">Functions</a></h2>
<p>
<a name="fn-clampcolorcomponent"></a>
<a class="mochidef reference" href="#fn-clampcolorcomponent">clampColorComponent(num, scale)</a>:</p>
<blockquote>
<p>Returns <tt class="docutils literal"><span class="pre">num</span> <span class="pre">*</span> <span class="pre">scale</span></tt> clamped between <tt class="docutils literal"><span class="pre">0</span></tt> and <tt class="docutils literal"><span class="pre">scale</span></tt>.</p>
<p><a class="mochiref reference" href="#fn-clampcolorcomponent">clampColorComponent</a> is not exported by default when using JSAN.</p>
</blockquote>
<p>
<a name="fn-hsltorgb"></a>
<a class="mochidef reference" href="#fn-hsltorgb">hslToRGB(hue, saturation, lightness, alpha)</a>:</p>
<blockquote>
<p>Computes RGB values from the provided HSL values. The return value is a
mapping with <tt class="docutils literal"><span class="pre">&quot;r&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;g&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;b&quot;</span></tt> and <tt class="docutils literal"><span class="pre">&quot;a&quot;</span></tt> keys.</p>
<dl class="docutils">
<dt>Alternate form:</dt>
<dd><a class="mochiref reference" href="#fn-hsltorgb">hslToRGB({h: hue, s: saturation, l: lightness, a: alpha})</a>.</dd>
</dl>
<p><a class="mochiref reference" href="#fn-hsltorgb">hslToRGB</a> is not exported by default when using JSAN.</p>
</blockquote>
<p>
<a name="fn-hsvtorgb"></a>
<a class="mochidef reference" href="#fn-hsvtorgb">hsvToRGB(hue, saturation, value, alpha)</a>:</p>
<blockquote>
<p>Computes RGB values from the provided HSV values. The return value is a
mapping with <tt class="docutils literal"><span class="pre">&quot;r&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;g&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;b&quot;</span></tt> and <tt class="docutils literal"><span class="pre">&quot;a&quot;</span></tt> keys.</p>
<dl class="docutils">
<dt>Alternate form:</dt>
<dd><a class="mochiref reference" href="#fn-hsvtorgb">hsvToRGB({h: hue, s: saturation, v: value, a: alpha})</a>.</dd>
</dl>
<p><a class="mochiref reference" href="#fn-hsvtorgb">hsvToRGB</a> is not exported by default when using JSAN.</p>
</blockquote>
<p>
<a name="fn-tocolorpart"></a>
<a class="mochidef reference" href="#fn-tocolorpart">toColorPart(num)</a>:</p>
<blockquote>
<p>Convert num to a zero padded hexadecimal digit for use in a hexadecimal
color string. Num should be an integer between <tt class="docutils literal"><span class="pre">0</span></tt> and <tt class="docutils literal"><span class="pre">255</span></tt>.</p>
<p><a class="mochiref reference" href="#fn-tocolorpart">toColorPart</a> is not exported by default when using JSAN.</p>
</blockquote>
<p>
<a name="fn-rgbtohsl"></a>
<a class="mochidef reference" href="#fn-rgbtohsl">rgbToHSL(red, green, blue, alpha)</a>:</p>
<blockquote>
<p>Computes HSL values based on the provided RGB values. The return value is
a mapping with <tt class="docutils literal"><span class="pre">&quot;h&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;s&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;l&quot;</span></tt> and <tt class="docutils literal"><span class="pre">&quot;a&quot;</span></tt> keys.</p>
<dl class="docutils">
<dt>Alternate form:</dt>
<dd><a class="mochiref reference" href="#fn-rgbtohsl">rgbToHSL({r: red, g: green, b: blue, a: alpha})</a>.</dd>
</dl>
<p><a class="mochiref reference" href="#fn-rgbtohsl">rgbToHSL</a> is not exported by default when using JSAN.</p>
</blockquote>
<p>
<a name="fn-rgbtohsv"></a>
<a class="mochidef reference" href="#fn-rgbtohsv">rgbToHSV(red, green, blue, alpha)</a>:</p>
<blockquote>
<p>Computes HSV values based on the provided RGB values. The return value is
a mapping with <tt class="docutils literal"><span class="pre">&quot;h&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;s&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;v&quot;</span></tt> and <tt class="docutils literal"><span class="pre">&quot;a&quot;</span></tt> keys.</p>
<dl class="docutils">
<dt>Alternate form:</dt>
<dd><a class="mochiref reference" href="#fn-rgbtohsv">rgbToHSV({r: red, g: green, b: blue, a: alpha})</a>.</dd>
</dl>
<p><a class="mochiref reference" href="#fn-rgbtohsv">rgbToHSV</a> is not exported by default when using JSAN.</p>
</blockquote>
</div>
</div>
<div class="section">
<h1><a id="see-also" name="see-also">See Also</a></h1>
<table class="docutils footnote" frame="void" id="id6" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1" name="id6">[1]</a></td><td>Application Kit Reference - NSColor: <a class="reference" href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Classes/NSColor.html">http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Classes/NSColor.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id7" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a name="id7">[2]</a></td><td><em>(<a class="fn-backref" href="#id2">1</a>, <a class="fn-backref" href="#id4">2</a>)</em> SVG 1.0 color keywords: <a class="reference" href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">http://www.w3.org/TR/SVG/types.html#ColorKeywords</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id8" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a name="id8">[3]</a></td><td><em>(<a class="fn-backref" href="#id3">1</a>, <a class="fn-backref" href="#id5">2</a>)</em> W3C CSS3 Color Module: <a class="reference" href="http://www.w3.org/TR/css3-color/#svg-color">http://www.w3.org/TR/css3-color/#svg-color</a></td></tr>
</tbody>
</table>
</div>
<div class="section">
<h1><a id="authors" name="authors">Authors</a></h1>
<ul class="simple">
<li>Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;</li>
</ul>
</div>
<div class="section">
<h1><a id="copyright" name="copyright">Copyright</a></h1>
<p>Copyright 2005 Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;. This program is dual-licensed
free software; you can redistribute it and/or modify it under the terms of the
<a class="reference" href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or the <a class="reference" href="http://www.opensource.org/licenses/afl-2.1.php">Academic Free License v2.1</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -1,798 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
<title>MochiKit.DOM - painless DOM manipulation API</title>
<link rel="stylesheet" href="../../../include/css/documentation.css" type="text/css" />
<script type="text/javascript" src="../../../packed/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="../../js/toc.js"></script>
</head>
<body>
<div class="document">
<div class="section">
<h1><a id="name" name="name">Name</a></h1>
<p>MochiKit.DOM - painless DOM manipulation API</p>
</div>
<div class="section">
<h1><a id="synopsis" name="synopsis">Synopsis</a></h1>
<pre class="literal-block">
var rows = [
[&quot;dataA1&quot;, &quot;dataA2&quot;, &quot;dataA3&quot;],
[&quot;dataB1&quot;, &quot;dataB2&quot;, &quot;dataB3&quot;]
];
row_display = function (row) {
return TR(null, map(partial(TD, null), row));
}
var newTable = TABLE({'class': 'prettytable'},
THEAD(null,
row_display([&quot;head1&quot;, &quot;head2&quot;, &quot;head3&quot;])),
TFOOT(null,
row_display([&quot;foot1&quot;, &quot;foot2&quot;, &quot;foot3&quot;])),
TBODY(null,
map(row_display, rows)));
// put that in your document.createElement and smoke it!
swapDOM(oldTable, newTable);
</pre>
</div>
<div class="section">
<h1><a id="description" name="description">Description</a></h1>
<p>As you probably know, the DOM APIs are some of the most painful Java-inspired
APIs you'll run across from a highly dynamic language. Don't worry about that
though, because they provide a reasonable basis to build something that
sucks a lot less.</p>
<p>MochiKit.DOM takes much of its inspiration from Nevow's <a class="footnote-reference" href="#id5" id="id1" name="id1">[1]</a> stan <a class="footnote-reference" href="#id6" id="id2" name="id2">[2]</a>.
This means you choose a tag, give it some attributes, then stuff it full
of <em>whatever objects you want</em>. MochiKit.DOM isn't stupid, it knows that
a string should be a text node, and that you want functions to be called,
and that <tt class="docutils literal"><span class="pre">Array</span></tt>-like objects should be expanded, and stupid <tt class="docutils literal"><span class="pre">null</span></tt> values
should be skipped.</p>
<p>Hell, it will let you return strings from functions, and use iterators from
<a class="mochiref reference" href="Iter.html">MochiKit.Iter</a>. If that's not enough, just teach it new tricks with
<a class="mochiref reference" href="#fn-registerdomconverter">registerDOMConverter</a>. If you have never used an API like this for
creating DOM elements, you've been wasting your damn time. Get with it!</p>
</div>
<div class="section">
<h1><a id="dependencies" name="dependencies">Dependencies</a></h1>
<ul class="simple">
<li><a class="mochiref reference" href="Base.html">MochiKit.Base</a></li>
<li><a class="mochiref reference" href="Iter.html">MochiKit.Iter</a></li>
</ul>
</div>
<div class="section">
<h1><a id="overview" name="overview">Overview</a></h1>
<div class="section">
<h2><a id="dom-coercion-rules" name="dom-coercion-rules">DOM Coercion Rules</a></h2>
<p>In order of precedence, <a class="mochiref reference" href="#fn-createdom">createDOM</a> coerces given arguments to DOM
nodes using the following rules:</p>
<ol class="arabic simple">
<li>Functions are called with a <tt class="docutils literal"><span class="pre">this</span></tt> of the parent
node and their return value is subject to the
following rules (even this one).</li>
<li><tt class="docutils literal"><span class="pre">undefined</span></tt> and <tt class="docutils literal"><span class="pre">null</span></tt> are ignored.</li>
<li>Iterables (see <a class="mochiref reference" href="Iter.html">MochiKit.Iter</a>) are flattened
(as if they were passed in-line as nodes) and each
return value is subject to all of these rules.</li>
<li>Values that look like DOM nodes (objects with a
<tt class="docutils literal"><span class="pre">.nodeType</span> <span class="pre">&gt;</span> <span class="pre">0</span></tt>) are <tt class="docutils literal"><span class="pre">.appendChild</span></tt>'ed to the created
DOM fragment.</li>
<li>Strings are wrapped up with <tt class="docutils literal"><span class="pre">document.createTextNode</span></tt></li>
<li>Objects that are not strings are run through the <tt class="docutils literal"><span class="pre">domConverters</span></tt>
<a class="mochiref reference" href="Base.html#fn-adapterregistry">MochiKit.Base.AdapterRegistry</a>
(see <a class="mochiref reference" href="#fn-registerdomconverter">registerDOMConverter</a>).
The value returned by the adapter is subject to these same rules (e.g.
adapters are allowed to return a string, which will be coerced into a
text node).</li>
<li>If no adapter is available, <tt class="docutils literal"><span class="pre">.toString()</span></tt> is used to create a text node.</li>
</ol>
</div>
<div class="section">
<h2><a id="creating-dom-element-trees" name="creating-dom-element-trees">Creating DOM Element Trees</a></h2>
<p><a class="mochiref reference" href="#fn-createdom">createDOM</a> provides you with an excellent facility for creating DOM trees
that is easy on the wrists. One of the best ways to understand how to use
it is to take a look at an example:</p>
<pre class="literal-block">
var rows = [
[&quot;dataA1&quot;, &quot;dataA2&quot;, &quot;dataA3&quot;],
[&quot;dataB1&quot;, &quot;dataB2&quot;, &quot;dataB3&quot;]
];
row_display = function (row) {
return TR(null, map(partial(TD, null), row));
}
var newTable = TABLE({'class': 'prettytable'},
THEAD(null,
row_display([&quot;head1&quot;, &quot;head2&quot;, &quot;head3&quot;])),
TFOOT(null,
row_display([&quot;foot1&quot;, &quot;foot2&quot;, &quot;foot3&quot;])),
TBODY(null,
map(row_display, rows)));
</pre>
<p>This will create a table with the following visual layout (if it
were inserted into the document DOM):</p>
<blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="33%" />
<col width="33%" />
<col width="33%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">head1</th>
<th class="head">head2</th>
<th class="head">head3</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>dataA1</td>
<td>dataA2</td>
<td>dataA3</td>
</tr>
<tr><td>dataB1</td>
<td>dataB2</td>
<td>dataB3</td>
</tr>
<tr><td>foot1</td>
<td>foot2</td>
<td>foot3</td>
</tr>
</tbody>
</table>
</blockquote>
<p>Corresponding to the following HTML:</p>
<pre class="literal-block">
&lt;table class=&quot;prettytable&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;head1&lt;/td&gt;
&lt;td&gt;head2&lt;/td&gt;
&lt;td&gt;head3&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tfoot&gt;
&lt;tr&gt;
&lt;td&gt;foot1&lt;/td&gt;
&lt;td&gt;foot2&lt;/td&gt;
&lt;td&gt;foot3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tfoot&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;dataA1&lt;/td&gt;
&lt;td&gt;dataA2&lt;/td&gt;
&lt;td&gt;dataA3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dataB1&lt;/td&gt;
&lt;td&gt;dataB2&lt;/td&gt;
&lt;td&gt;dataB3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</pre>
</div>
<div class="section">
<h2><a id="dom-context" name="dom-context">DOM Context</a></h2>
<p>In order to prevent having to pass a <tt class="docutils literal"><span class="pre">window</span></tt> and/or <tt class="docutils literal"><span class="pre">document</span></tt>
variable to every MochiKit.DOM function (e.g. when working with a
child window), MochiKit.DOM maintains a context variable for each
of them. They are managed with the <a class="mochiref reference" href="#fn-withwindow">withWindow</a> and
<a class="mochiref reference" href="#fn-withdocument">withDocument</a> functions, and can be acquired with
<a class="mochiref reference" href="#fn-currentwindow">currentWindow()</a> and <a class="mochiref reference" href="#fn-currentdocument">currentDocument()</a></p>
<p>For example, if you are creating DOM nodes in a child window, you
could do something like this:</p>
<pre class="literal-block">
withWindow(child, function () {
var doc = currentDocument();
appendChildNodes(doc.body, H1(null, &quot;This is in the child!&quot;));
});
</pre>
<p>Note that <a class="mochiref reference" href="#fn-withwindow">withWindow(win, ...)</a> also implies
<a class="mochiref reference" href="#fn-withdocument">withDocument(win.document, ...)</a>.</p>
</div>
<div class="section">
<h2><a id="element-visibility" name="element-visibility">Element Visibility</a></h2>
<p>The <a class="mochiref reference" href="#fn-hideelement">hideElement</a> and <a class="mochiref reference" href="#fn-showelement">showElement</a> functions are
provided as a convenience, but only work for elements that are
<tt class="docutils literal"><span class="pre">display:</span> <span class="pre">block</span></tt>. For a general solution to showing, hiding, and checking
the explicit visibility of elements, we recommend using a solution that
involves a little CSS. Here's an example:</p>
<pre class="literal-block">
&lt;style type=&quot;text/css&quot;&gt;
.invisible { display: none; }
&lt;/style&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
function toggleVisible(elem) {
toggleElementClass(&quot;invisible&quot;, elem);
}
function makeVisible(elem) {
removeElementClass(elem, &quot;invisible&quot;);
}
function makeInvisible(elem) {
addElementClass(elem, &quot;invisible&quot;);
}
function isVisible(elem) {
// you may also want to check for
// getElement(elem).style.display == &quot;none&quot;
return !hasElementClass(elem, &quot;invisible&quot;);
};
&lt;/script&gt;
</pre>
<p>MochiKit doesn't ship with such a solution, because there is no reliable and
portable method for adding CSS rules on the fly with JavaScript.</p>
</div>
</div>
<div class="section">
<h1><a id="api-reference" name="api-reference">API Reference</a></h1>
<div class="section">
<h2><a id="functions" name="functions">Functions</a></h2>
<p>
<a name="fn-$"></a>
<a class="mochidef reference" href="#fn-$">$(id[, ...])</a>:</p>
<blockquote>
An alias for <a class="mochiref reference" href="#fn-getelement">getElement(id[, ...])</a></blockquote>
<p>
<a name="fn-addelementclass"></a>
<a class="mochidef reference" href="#fn-addelementclass">addElementClass(element, className)</a>:</p>
<blockquote>
Ensure that the given <tt class="docutils literal"><span class="pre">element</span></tt> has <tt class="docutils literal"><span class="pre">className</span></tt> set as part of its
class attribute. This will not disturb other class names.
<tt class="docutils literal"><span class="pre">element</span></tt> is looked up with <a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers
are also acceptable.</blockquote>
<p>
<a name="fn-addloadevent"></a>
<a class="mochidef reference" href="#fn-addloadevent">addLoadEvent(func)</a>:</p>
<blockquote>
<p>Note that <a class="mochiref reference" href="#fn-addloadevent">addLoadEvent</a> can not be used in combination with
<a class="mochiref reference" href="Signal.html">MochiKit.Signal</a> if the <tt class="docutils literal"><span class="pre">onload</span></tt> event is connected.
Once an event is connected with <a class="mochiref reference" href="Signal.html">MochiKit.Signal</a>, no other APIs
may be used for that same event.</p>
<p>This will stack <tt class="docutils literal"><span class="pre">window.onload</span></tt> functions on top of each other.
Each function added will be called after <tt class="docutils literal"><span class="pre">onload</span></tt> in the
order that they were added.</p>
</blockquote>
<p>
<a name="fn-addtocallstack"></a>
<a class="mochidef reference" href="#fn-addtocallstack">addToCallStack(target, path, func[, once])</a>:</p>
<blockquote>
<p>Note that <a class="mochiref reference" href="#fn-addtocallstack">addToCallStack</a> is not compatible with
<a class="mochiref reference" href="Signal.html">MochiKit.Signal</a>. Once an event is connected with
<a class="mochiref reference" href="Signal.html">MochiKit.Signal</a>, no other APIs may be used for that same event.</p>
<p>Set the property <tt class="docutils literal"><span class="pre">path</span></tt> of <tt class="docutils literal"><span class="pre">target</span></tt> to a function that calls the
existing function at that property (if any), then calls <tt class="docutils literal"><span class="pre">func</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">target[path]()</span></tt> returns exactly <tt class="docutils literal"><span class="pre">false</span></tt>, then <tt class="docutils literal"><span class="pre">func</span></tt> will
not be called.</p>
<p>If <tt class="docutils literal"><span class="pre">once</span></tt> is <tt class="docutils literal"><span class="pre">true</span></tt>, then <tt class="docutils literal"><span class="pre">target[path]</span></tt> is set to <tt class="docutils literal"><span class="pre">null</span></tt> after
the function call stack has completed.</p>
<p>If called several times for the same <tt class="docutils literal"><span class="pre">target[path]</span></tt>, it will create
a stack of functions (instead of just a pair).</p>
</blockquote>
<p>
<a name="fn-appendchildnodes"></a>
<a class="mochidef reference" href="#fn-appendchildnodes">appendChildNodes(node[, childNode[, ...]])</a>:</p>
<blockquote>
<p>Append children to a DOM element using the <a class="reference" href="#dom-coercion-rules">DOM Coercion Rules</a>.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>A reference to the DOM element to add children to
(if a string is given, <a class="mochiref reference" href="#fn-getelement">getElement(node)</a>
will be used to locate the node)</dd>
<dt><tt class="docutils literal"><span class="pre">childNode</span></tt>...:</dt>
<dd>All additional arguments, if any, will be coerced into DOM
nodes that are appended as children using the
<a class="reference" href="#dom-coercion-rules">DOM Coercion Rules</a>.</dd>
<dt><em>returns</em>:</dt>
<dd>The given DOM element</dd>
</dl>
</blockquote>
<p>
<a name="fn-computedstyle"></a>
<a class="mochidef reference" href="#fn-computedstyle">computedStyle(htmlElement, cssProperty, mozillaEquivalentCSS)</a>:</p>
<blockquote>
Looks up a CSS property for the given element. The element can be
specified as either a string with the element's ID or the element
object itself.</blockquote>
<p>
<a name="fn-createdom"></a>
<a class="mochidef reference" href="#fn-createdom">createDOM(name[, attrs[, node[, ...]]])</a>:</p>
<blockquote>
<p>Create a DOM fragment in a really convenient manner, much like
Nevow`s <a class="footnote-reference" href="#id5" id="id3" name="id3">[1]</a> stan <a class="footnote-reference" href="#id6" id="id4" name="id4">[2]</a>.</p>
<p>Partially applied versions of this function for common tags are
available as aliases:</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">A</span></tt></li>
<li><tt class="docutils literal"><span class="pre">BUTTON</span></tt></li>
<li><tt class="docutils literal"><span class="pre">BR</span></tt></li>
<li><tt class="docutils literal"><span class="pre">CANVAS</span></tt></li>
<li><tt class="docutils literal"><span class="pre">DIV</span></tt></li>
<li><tt class="docutils literal"><span class="pre">FIELDSET</span></tt></li>
<li><tt class="docutils literal"><span class="pre">FORM</span></tt></li>
<li><tt class="docutils literal"><span class="pre">H1</span></tt></li>
<li><tt class="docutils literal"><span class="pre">H2</span></tt></li>
<li><tt class="docutils literal"><span class="pre">H3</span></tt></li>
<li><tt class="docutils literal"><span class="pre">HR</span></tt></li>
<li><tt class="docutils literal"><span class="pre">IMG</span></tt></li>
<li><tt class="docutils literal"><span class="pre">INPUT</span></tt></li>
<li><tt class="docutils literal"><span class="pre">LABEL</span></tt></li>
<li><tt class="docutils literal"><span class="pre">LEGEND</span></tt></li>
<li><tt class="docutils literal"><span class="pre">LI</span></tt></li>
<li><tt class="docutils literal"><span class="pre">OL</span></tt></li>
<li><tt class="docutils literal"><span class="pre">OPTGROUP</span></tt></li>
<li><tt class="docutils literal"><span class="pre">OPTION</span></tt></li>
<li><tt class="docutils literal"><span class="pre">P</span></tt></li>
<li><tt class="docutils literal"><span class="pre">PRE</span></tt></li>
<li><tt class="docutils literal"><span class="pre">SELECT</span></tt></li>
<li><tt class="docutils literal"><span class="pre">SPAN</span></tt></li>
<li><tt class="docutils literal"><span class="pre">STRONG</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TABLE</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TBODY</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TD</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TEXTAREA</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TFOOT</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TH</span></tt></li>
<li><tt class="docutils literal"><span class="pre">THEAD</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TR</span></tt></li>
<li><tt class="docutils literal"><span class="pre">TT</span></tt></li>
<li><tt class="docutils literal"><span class="pre">UL</span></tt></li>
</ul>
<p>See <a class="reference" href="#creating-dom-element-trees">Creating DOM Element Trees</a> for a comprehensive example.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">name</span></tt>:</dt>
<dd>The kind of fragment to create (e.g. 'span'), such as you would
pass to <tt class="docutils literal"><span class="pre">document.createElement</span></tt>.</dd>
<dt><tt class="docutils literal"><span class="pre">attrs</span></tt>:</dt>
<dd><p class="first">An object whose properties will be used as the attributes
(e.g. <tt class="docutils literal"><span class="pre">{'style':</span> <span class="pre">'display:block'}</span></tt>), or <tt class="docutils literal"><span class="pre">null</span></tt> if no
attributes need to be set.</p>
<p>See <a class="mochiref reference" href="#fn-updatenodeattributes">updateNodeAttributes</a> for more information.</p>
<p class="last">For convenience, if <tt class="docutils literal"><span class="pre">attrs</span></tt> is a string, <tt class="docutils literal"><span class="pre">null</span></tt> is used
and the string will be considered the first <tt class="docutils literal"><span class="pre">node</span></tt>.</p>
</dd>
<dt><tt class="docutils literal"><span class="pre">node</span></tt>...:</dt>
<dd>All additional arguments, if any, will be coerced into DOM
nodes that are appended as children using the
<a class="reference" href="#dom-coercion-rules">DOM Coercion Rules</a>.</dd>
<dt><em>returns</em>:</dt>
<dd>A DOM element</dd>
</dl>
</blockquote>
<p>
<a name="fn-createdomfunc"></a>
<a class="mochidef reference" href="#fn-createdomfunc">createDOMFunc(tag[, attrs[, node[, ...]]])</a>:</p>
<blockquote>
<p>Convenience function to create a partially applied createDOM
function. You'd want to use this if you add additional convenience
functions for creating tags, or if you find yourself creating
a lot of tags with a bunch of the same attributes or contents.</p>
<p>See <a class="mochiref reference" href="#fn-createdom">createDOM</a> for more detailed descriptions of the arguments.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">tag</span></tt>:</dt>
<dd>The name of the tag</dd>
<dt><tt class="docutils literal"><span class="pre">attrs</span></tt>:</dt>
<dd>Optionally specify the attributes to apply</dd>
<dt><tt class="docutils literal"><span class="pre">node</span></tt>...:</dt>
<dd>Optionally specify any children nodes it should have</dd>
<dt><em>returns</em>:</dt>
<dd>function that takes additional arguments and calls
<a class="mochiref reference" href="#fn-createdom">createDOM</a></dd>
</dl>
</blockquote>
<p>
<a name="fn-currentdocument"></a>
<a class="mochidef reference" href="#fn-currentdocument">currentDocument()</a>:</p>
<blockquote>
Return the current <tt class="docutils literal"><span class="pre">document</span></tt> <a class="reference" href="#dom-context">DOM Context</a>. This will always
be the same as the global <tt class="docutils literal"><span class="pre">document</span></tt> unless <a class="mochiref reference" href="#fn-withdocument">withDocument</a> or
<a class="mochiref reference" href="#fn-withwindow">withWindow</a> is currently executing.</blockquote>
<p>
<a name="fn-currentwindow"></a>
<a class="mochidef reference" href="#fn-currentwindow">currentWindow()</a>:</p>
<blockquote>
Return the current <tt class="docutils literal"><span class="pre">window</span></tt> <a class="reference" href="#dom-context">DOM Context</a>. This will always
be the same as the global <tt class="docutils literal"><span class="pre">window</span></tt> unless <a class="mochiref reference" href="#fn-withwindow">withWindow</a> is
currently executing.</blockquote>
<p>
<a name="fn-elementdimensions"></a>
<a class="mochidef reference" href="#fn-elementdimensions">elementDimensions(element)</a>:</p>
<blockquote>
Return the absolute pixel width and height of <tt class="docutils literal"><span class="pre">element</span></tt> as an object with
<tt class="docutils literal"><span class="pre">w</span></tt> and <tt class="docutils literal"><span class="pre">h</span></tt> properties, or <tt class="docutils literal"><span class="pre">undefined</span></tt> if <tt class="docutils literal"><span class="pre">element</span></tt> is not in the
document. <tt class="docutils literal"><span class="pre">element</span></tt> may be specified as a string to be looked up with
<a class="mochiref reference" href="#fn-getelement">getElement</a>, a DOM element, or trivially as an object with
<tt class="docutils literal"><span class="pre">w</span></tt> and/or <tt class="docutils literal"><span class="pre">h</span></tt> properties.</blockquote>
<p>
<a name="fn-elementposition"></a>
<a class="mochidef reference" href="#fn-elementposition">elementPosition(element[, relativeTo={x: 0, y: 0}])</a>:</p>
<blockquote>
<p>Return the absolute pixel position of <tt class="docutils literal"><span class="pre">element</span></tt> in the document as an
object with <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt> properties, or <tt class="docutils literal"><span class="pre">undefined</span></tt> if <tt class="docutils literal"><span class="pre">element</span></tt>
is not in the document. <tt class="docutils literal"><span class="pre">element</span></tt> may be specified as a string to
be looked up with <a class="mochiref reference" href="#fn-getelement">getElement</a>, a DOM element, or trivially
as an object with <tt class="docutils literal"><span class="pre">x</span></tt> and/or <tt class="docutils literal"><span class="pre">y</span></tt> properties.</p>
<p>If <tt class="docutils literal"><span class="pre">relativeTo</span></tt> is given, then its coordinates are subtracted from
the absolute position of <tt class="docutils literal"><span class="pre">element</span></tt>, e.g.:</p>
<pre class="literal-block">
var elemPos = elementPosition(elem);
var anotherElemPos = elementPosition(anotherElem);
var relPos = elementPosition(elem, anotherElem);
assert( relPos.x == (elemPos.x - anotherElemPos.x) );
assert( relPos.y == (elemPos.y - anotherElemPos.y) );
</pre>
<p><tt class="docutils literal"><span class="pre">relativeTo</span></tt> may be specified as a string to be looked up with
<a class="mochiref reference" href="#fn-getelement">getElement</a>, a DOM element, or trivially as an object
with <tt class="docutils literal"><span class="pre">x</span></tt> and/or <tt class="docutils literal"><span class="pre">y</span></tt> properties.</p>
</blockquote>
<p>
<a name="fn-emithtml"></a>
<a class="mochidef reference" href="#fn-emithtml">emitHTML(dom[, lst])</a>:</p>
<blockquote>
<p>Convert a DOM tree to an <tt class="docutils literal"><span class="pre">Array</span></tt> of HTML string fragments</p>
<p>You probably want to use <a class="mochiref reference" href="#fn-tohtml">toHTML</a> instead.</p>
</blockquote>
<p>
<a name="fn-escapehtml"></a>
<a class="mochidef reference" href="#fn-escapehtml">escapeHTML(s)</a>:</p>
<blockquote>
Make a string safe for HTML, converting the usual suspects (lt,
gt, quot, apos, amp)</blockquote>
<p>
<a name="fn-focusonload"></a>
<a class="mochidef reference" href="#fn-focusonload">focusOnLoad(element)</a>:</p>
<blockquote>
Add an onload event to focus the given element</blockquote>
<p>
<a name="fn-formcontents"></a>
<a class="mochidef reference" href="#fn-formcontents">formContents(elem)</a>:</p>
<blockquote>
Search the DOM tree, starting at <tt class="docutils literal"><span class="pre">elem</span></tt>, for any elements with a
<tt class="docutils literal"><span class="pre">name</span></tt> and <tt class="docutils literal"><span class="pre">value</span></tt> attribute. Return a 2-element <tt class="docutils literal"><span class="pre">Array</span></tt> of
<tt class="docutils literal"><span class="pre">names</span></tt> and <tt class="docutils literal"><span class="pre">values</span></tt> suitable for use with
<a class="mochiref reference" href="Base.html#fn-querystring">MochiKit.Base.queryString</a>.</blockquote>
<p>
<a name="fn-getelement"></a>
<a class="mochidef reference" href="#fn-getelement">getElement(id[, ...])</a>:</p>
<blockquote>
<p>A small quick little function to encapsulate the <tt class="docutils literal"><span class="pre">getElementById</span></tt>
method. It includes a check to ensure we can use that method.</p>
<p>If the id isn't a string, it will be returned as-is.</p>
<p>Also available as <a class="mochiref reference" href="#fn-$">$(...)</a> for convenience and compatibility with
other JavaScript frameworks.</p>
<p>If multiple arguments are given, an <tt class="docutils literal"><span class="pre">Array</span></tt> will be returned.</p>
</blockquote>
<p>
<a name="fn-getelementsbytagandclassname"></a>
<a class="mochidef reference" href="#fn-getelementsbytagandclassname">getElementsByTagAndClassName(tagName, className, parent=document)</a>:</p>
<blockquote>
<p>Returns an array of elements in <tt class="docutils literal"><span class="pre">parent</span></tt> that match the tag name
and class name provided. If <tt class="docutils literal"><span class="pre">parent</span></tt> is a string, it will be looked
up with <a class="mochiref reference" href="#fn-getelement">getElement</a>.</p>
<p>If <tt class="docutils literal"><span class="pre">tagName</span></tt> is <tt class="docutils literal"><span class="pre">null</span></tt> or <tt class="docutils literal"><span class="pre">&quot;*&quot;</span></tt>, all elements will be searched
for the matching class.</p>
<p>If <tt class="docutils literal"><span class="pre">className</span></tt> is <tt class="docutils literal"><span class="pre">null</span></tt>, all elements matching the provided tag are
returned.</p>
</blockquote>
<p>
<a name="fn-getnodeattribute"></a>
<a class="mochidef reference" href="#fn-getnodeattribute">getNodeAttribute(node, attr)</a>:</p>
<blockquote>
<p>Get the value of the given attribute for a DOM element without
ever raising an exception (will return <tt class="docutils literal"><span class="pre">null</span></tt> on exception).</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>A reference to the DOM element to update (if a string is given,
<a class="mochiref reference" href="#fn-getelement">getElement(node)</a> will be used to locate the node)</dd>
<dt><tt class="docutils literal"><span class="pre">attr</span></tt>:</dt>
<dd><p class="first">The name of the attribute</p>
<p class="last">Note that it will do the right thing for IE, so don't do
the <tt class="docutils literal"><span class="pre">class</span></tt> -&gt; <tt class="docutils literal"><span class="pre">className</span></tt> hack yourself.</p>
</dd>
<dt><em>returns</em>:</dt>
<dd>The attribute's value, or <tt class="docutils literal"><span class="pre">null</span></tt></dd>
</dl>
</blockquote>
<p>
<a name="fn-getviewportdimensions"></a>
<a class="mochidef reference" href="#fn-getviewportdimensions">getViewportDimensions()</a>:</p>
<blockquote>
Return the pixel width and height of the viewport as an object with <tt class="docutils literal"><span class="pre">w</span></tt>
and <tt class="docutils literal"><span class="pre">h</span></tt> properties. <tt class="docutils literal"><span class="pre">element</span></tt> is looked up with
<a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers are also acceptable.</blockquote>
<p>
<a name="fn-haselementclass"></a>
<a class="mochidef reference" href="#fn-haselementclass">hasElementClass(element, className[, ...])</a>:</p>
<blockquote>
Return <tt class="docutils literal"><span class="pre">true</span></tt> if <tt class="docutils literal"><span class="pre">className</span></tt> is found on the <tt class="docutils literal"><span class="pre">element</span></tt>.
<tt class="docutils literal"><span class="pre">element</span></tt> is looked up with <a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers
are also acceptable.</blockquote>
<p>
<a name="fn-hideelement"></a>
<a class="mochidef reference" href="#fn-hideelement">hideElement(element, ...)</a>:</p>
<blockquote>
<p>Partial form of <a class="mochiref reference" href="#fn-setdisplayforelement">setDisplayForElement</a>, specifically:</p>
<pre class="literal-block">
partial(setDisplayForElement, &quot;none&quot;)
</pre>
<p>For information about the caveats of using a <tt class="docutils literal"><span class="pre">style.display</span></tt> based
show/hide mechanism, and a CSS based alternative, see
<a class="reference" href="#element-visibility">Element Visibility</a>.</p>
</blockquote>
<p>
<a name="fn-registerdomconverter"></a>
<a class="mochidef reference" href="#fn-registerdomconverter">registerDOMConverter(name, check, wrap[, override])</a>:</p>
<blockquote>
Register an adapter to convert objects that match <tt class="docutils literal"><span class="pre">check(obj,</span> <span class="pre">ctx)</span></tt>
to a DOM element, or something that can be converted to a DOM
element (i.e. number, bool, string, function, iterable).</blockquote>
<p>
<a name="fn-removeelement"></a>
<a class="mochidef reference" href="#fn-removeelement">removeElement(node)</a>:</p>
<blockquote>
<p>Remove and return <tt class="docutils literal"><span class="pre">node</span></tt> from a DOM tree. This is technically
just a convenience for <a class="mochiref reference" href="#fn-swapdom">swapDOM(node, null)</a>.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>the DOM element (or string id of one) to be removed</dd>
<dt><em>returns</em></dt>
<dd>The removed element</dd>
</dl>
</blockquote>
<p>
<a name="fn-removeelementclass"></a>
<a class="mochidef reference" href="#fn-removeelementclass">removeElementClass(element, className)</a>:</p>
<blockquote>
Ensure that the given <tt class="docutils literal"><span class="pre">element</span></tt> does not have <tt class="docutils literal"><span class="pre">className</span></tt> set as part
of its class attribute. This will not disturb other class names.
<tt class="docutils literal"><span class="pre">element</span></tt> is looked up with <a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers
are also acceptable.</blockquote>
<p>
<a name="fn-replacechildnodes"></a>
<a class="mochidef reference" href="#fn-replacechildnodes">replaceChildNodes(node[, childNode[, ...]])</a>:</p>
<blockquote>
<p>Remove all children from the given DOM element, then append any given
childNodes to it (by calling <a class="mochiref reference" href="#fn-appendchildnodes">appendChildNodes</a>).</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>A reference to the DOM element to add children to
(if a string is given, <a class="mochiref reference" href="#fn-getelement">getElement(node)</a>
will be used to locate the node)</dd>
<dt><tt class="docutils literal"><span class="pre">childNode</span></tt>...:</dt>
<dd>All additional arguments, if any, will be coerced into DOM
nodes that are appended as children using the
<a class="reference" href="#dom-coercion-rules">DOM Coercion Rules</a>.</dd>
<dt><em>returns</em>:</dt>
<dd>The given DOM element</dd>
</dl>
</blockquote>
<p>
<a name="fn-scrapetext"></a>
<a class="mochidef reference" href="#fn-scrapetext">scrapeText(node[, asArray=false])</a>:</p>
<blockquote>
<p>Walk a DOM tree in-order and scrape all of the text out of it as a
<tt class="docutils literal"><span class="pre">string</span></tt>.</p>
<p>If <tt class="docutils literal"><span class="pre">asArray</span></tt> is <tt class="docutils literal"><span class="pre">true</span></tt>, then an <tt class="docutils literal"><span class="pre">Array</span></tt> will be returned with
each individual text node. These two are equivalent:</p>
<pre class="literal-block">
assert( scrapeText(node) == scrapeText(node, true).join(&quot;&quot;) );
</pre>
</blockquote>
<p>
<a name="fn-setdisplayforelement"></a>
<a class="mochidef reference" href="#fn-setdisplayforelement">setDisplayForElement(display, element[, ...])</a>:</p>
<blockquote>
<p>Change the <tt class="docutils literal"><span class="pre">style.display</span></tt> for the given element(s). Usually
used as the partial forms:</p>
<ul class="simple">
<li><a class="mochiref reference" href="#fn-showelement">showElement(element, ...)</a></li>
<li><a class="mochiref reference" href="#fn-hideelement">hideElement(element, ...)</a></li>
</ul>
<p>Elements are looked up with <a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers
are acceptable.</p>
<p>For information about the caveats of using a <tt class="docutils literal"><span class="pre">style.display</span></tt> based
show/hide mechanism, and a CSS based alternative, see
<a class="reference" href="#element-visibility">Element Visibility</a>.</p>
</blockquote>
<p>
<a name="fn-setelementclass"></a>
<a class="mochidef reference" href="#fn-setelementclass">setElementClass(element, className)</a>:</p>
<blockquote>
Set the entire class attribute of <tt class="docutils literal"><span class="pre">element</span></tt> to <tt class="docutils literal"><span class="pre">className</span></tt>.
<tt class="docutils literal"><span class="pre">element</span></tt> is looked up with <a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers
are also acceptable.</blockquote>
<p>
<a name="fn-setelementdimensions"></a>
<a class="mochidef reference" href="#fn-setelementdimensions">setElementDimensions(element, dimensions[, units='px'])</a>:</p>
<blockquote>
<p>Sets the dimensions of <tt class="docutils literal"><span class="pre">element</span></tt> in the document from an
object with <tt class="docutils literal"><span class="pre">w</span></tt> and <tt class="docutils literal"><span class="pre">h</span></tt> properties.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>A reference to the DOM element to update (if a string is given,
<a class="mochiref reference" href="#fn-getelement">getElement(node)</a> will be used to locate the node)</dd>
<dt><tt class="docutils literal"><span class="pre">dimensions</span></tt>:</dt>
<dd>An object with <tt class="docutils literal"><span class="pre">w</span></tt> and <tt class="docutils literal"><span class="pre">h</span></tt> properties</dd>
<dt><tt class="docutils literal"><span class="pre">units</span></tt>:</dt>
<dd>Optionally set the units to use, default is <tt class="docutils literal"><span class="pre">px</span></tt></dd>
</dl>
</blockquote>
<p>
<a name="fn-setelementposition"></a>
<a class="mochidef reference" href="#fn-setelementposition">setElementPosition(element, position[, units='px'])</a>:</p>
<blockquote>
<p>Sets the absolute position of <tt class="docutils literal"><span class="pre">element</span></tt> in the document from an
object with <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt> properties.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>A reference to the DOM element to update (if a string is given,
<a class="mochiref reference" href="#fn-getelement">getElement(node)</a> will be used to locate the node)</dd>
<dt><tt class="docutils literal"><span class="pre">position</span></tt>:</dt>
<dd>An object with <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt> properties</dd>
<dt><tt class="docutils literal"><span class="pre">units</span></tt>:</dt>
<dd>Optionally set the units to use, default is <tt class="docutils literal"><span class="pre">px</span></tt></dd>
</dl>
</blockquote>
<p>
<a name="fn-setnodeattribute"></a>
<a class="mochidef reference" href="#fn-setnodeattribute">setNodeAttribute(node, attr, value)</a>:</p>
<blockquote>
<p>Set the value of the given attribute for a DOM element without
ever raising an exception (will return null on exception). If
setting more than one attribute, you should use
<a class="mochiref reference" href="#fn-updatenodeattributes">updateNodeAttributes</a>.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>A reference to the DOM element to update (if a string is given,
<a class="mochiref reference" href="#fn-getelement">getElement(node)</a> will be used to locate the node)</dd>
<dt><tt class="docutils literal"><span class="pre">attr</span></tt>:</dt>
<dd><p class="first">The name of the attribute</p>
<p class="last">Note that it will do the right thing for IE, so don't do
the <tt class="docutils literal"><span class="pre">class</span></tt> -&gt; <tt class="docutils literal"><span class="pre">className</span></tt> hack yourself.</p>
</dd>
<dt><tt class="docutils literal"><span class="pre">value</span></tt>:</dt>
<dd>The value of the attribute, may be an object to be merged
(e.g. for setting style).</dd>
<dt><em>returns</em>:</dt>
<dd>The given DOM element or <tt class="docutils literal"><span class="pre">null</span></tt> on failure</dd>
</dl>
</blockquote>
<p>
<a name="fn-setopacity"></a>
<a class="mochidef reference" href="#fn-setopacity">setOpacity(element, opacity)</a>:</p>
<blockquote>
Sets <tt class="docutils literal"><span class="pre">opacity</span></tt> for <tt class="docutils literal"><span class="pre">element</span></tt>. Valid <tt class="docutils literal"><span class="pre">opacity</span></tt> values range from 0
(invisible) to 1 (opaque). <tt class="docutils literal"><span class="pre">element</span></tt> is looked up with
<a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers are also acceptable.</blockquote>
<p>
<a name="fn-showelement"></a>
<a class="mochidef reference" href="#fn-showelement">showElement(element, ...)</a>:</p>
<blockquote>
<p>Partial form of <a class="mochiref reference" href="#fn-setdisplayforelement">setDisplayForElement</a>, specifically:</p>
<pre class="literal-block">
partial(setDisplayForElement, &quot;block&quot;)
</pre>
<p>For information about the caveats of using a <tt class="docutils literal"><span class="pre">style.display</span></tt> based
show/hide mechanism, and a CSS based alternative, see
<a class="reference" href="#element-visibility">Element Visibility</a>.</p>
</blockquote>
<p>
<a name="fn-swapdom"></a>
<a class="mochidef reference" href="#fn-swapdom">swapDOM(dest, src)</a>:</p>
<blockquote>
<p>Replace <tt class="docutils literal"><span class="pre">dest</span></tt> in a DOM tree with <tt class="docutils literal"><span class="pre">src</span></tt>, returning <tt class="docutils literal"><span class="pre">src</span></tt>.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">dest</span></tt>:</dt>
<dd>a DOM element (or string id of one) to be replaced</dd>
<dt><tt class="docutils literal"><span class="pre">src</span></tt>:</dt>
<dd>the DOM element (or string id of one) to replace it with, or
<tt class="docutils literal"><span class="pre">null</span></tt> if <tt class="docutils literal"><span class="pre">dest</span></tt> is to be removed (replaced with nothing).</dd>
<dt><em>returns</em>:</dt>
<dd>a DOM element (<tt class="docutils literal"><span class="pre">src</span></tt>)</dd>
</dl>
</blockquote>
<p>
<a name="fn-swapelementclass"></a>
<a class="mochidef reference" href="#fn-swapelementclass">swapElementClass(element, fromClass, toClass)</a>:</p>
<blockquote>
If <tt class="docutils literal"><span class="pre">fromClass</span></tt> is set on <tt class="docutils literal"><span class="pre">element</span></tt>, replace it with <tt class="docutils literal"><span class="pre">toClass</span></tt>.
This will not disturb other classes on that element.
<tt class="docutils literal"><span class="pre">element</span></tt> is looked up with <a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers
are also acceptable.</blockquote>
<p>
<a name="fn-toggleelementclass"></a>
<a class="mochidef reference" href="#fn-toggleelementclass">toggleElementClass(className[, element[, ...]])</a>:</p>
<blockquote>
Toggle the presence of a given <tt class="docutils literal"><span class="pre">className</span></tt> in the class attribute
of all given elements. All elements will be looked up with
<a class="mochiref reference" href="#fn-getelement">getElement</a>, so string identifiers are acceptable.</blockquote>
<p>
<a name="fn-tohtml"></a>
<a class="mochidef reference" href="#fn-tohtml">toHTML(dom)</a>:</p>
<blockquote>
Convert a DOM tree to a HTML string using <a class="mochiref reference" href="#fn-emithtml">emitHTML</a></blockquote>
<p>
<a name="fn-updatenodeattributes"></a>
<a class="mochidef reference" href="#fn-updatenodeattributes">updateNodeAttributes(node, attrs)</a>:</p>
<blockquote>
<p>Update the attributes of a DOM element from a given object.</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">node</span></tt>:</dt>
<dd>A reference to the DOM element to update (if a string is given,
<a class="mochiref reference" href="#fn-getelement">getElement(node)</a> will be used to locate the node)</dd>
<dt><tt class="docutils literal"><span class="pre">attrs</span></tt>:</dt>
<dd><p class="first">An object whose properties will be used to set the attributes
(e.g. <tt class="docutils literal"><span class="pre">{'class':</span> <span class="pre">'invisible'}</span></tt>), or <tt class="docutils literal"><span class="pre">null</span></tt> if no
attributes need to be set. If an object is given for the
attribute value (e.g. <tt class="docutils literal"><span class="pre">{'style':</span> <span class="pre">{'display':</span> <span class="pre">'block'}}</span></tt>)
then <a class="mochiref reference" href="Base.html#fn-updatetree">MochiKit.Base.updatetree</a> will be used to set that
attribute.</p>
<p class="last">Note that it will do the right thing for IE, so don't do
the <tt class="docutils literal"><span class="pre">class</span></tt> -&gt; <tt class="docutils literal"><span class="pre">className</span></tt> hack yourself, and it deals with
setting &quot;on...&quot; event handlers correctly.</p>
</dd>
<dt><em>returns</em>:</dt>
<dd>The given DOM element</dd>
</dl>
</blockquote>
<p>
<a name="fn-withwindow"></a>
<a class="mochidef reference" href="#fn-withwindow">withWindow(win, func)</a>:</p>
<blockquote>
<p>Call <tt class="docutils literal"><span class="pre">func</span></tt> with the <tt class="docutils literal"><span class="pre">window</span></tt> <a class="reference" href="#dom-context">DOM Context</a> set to <tt class="docutils literal"><span class="pre">win</span></tt> and
the <tt class="docutils literal"><span class="pre">document</span></tt> <a class="reference" href="#dom-context">DOM Context</a> set to <tt class="docutils literal"><span class="pre">win.document</span></tt>. When
<tt class="docutils literal"><span class="pre">func()</span></tt> returns or throws an error, the <a class="reference" href="#dom-context">DOM Context</a> will be
restored to its previous state.</p>
<p>The return value of <tt class="docutils literal"><span class="pre">func()</span></tt> is returned by this function.</p>
</blockquote>
<p>
<a name="fn-withdocument"></a>
<a class="mochidef reference" href="#fn-withdocument">withDocument(doc, func)</a>:</p>
<blockquote>
<p>Call <tt class="docutils literal"><span class="pre">func</span></tt> with the <tt class="docutils literal"><span class="pre">doc</span></tt> <a class="reference" href="#dom-context">DOM Context</a> set to <tt class="docutils literal"><span class="pre">doc</span></tt>.
When <tt class="docutils literal"><span class="pre">func()</span></tt> returns or throws an error, the <a class="reference" href="#dom-context">DOM Context</a>
will be restored to its previous state.</p>
<p>The return value of <tt class="docutils literal"><span class="pre">func()</span></tt> is returned by this function.</p>
</blockquote>
</div>
</div>
<div class="section">
<h1><a id="see-also" name="see-also">See Also</a></h1>
<table class="docutils footnote" frame="void" id="id5" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a name="id5">[1]</a></td><td><em>(<a class="fn-backref" href="#id1">1</a>, <a class="fn-backref" href="#id3">2</a>)</em> Nevow, a web application construction kit for Python: <a class="reference" href="http://nevow.com/">http://nevow.com/</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id6" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a name="id6">[2]</a></td><td><em>(<a class="fn-backref" href="#id2">1</a>, <a class="fn-backref" href="#id4">2</a>)</em> nevow.stan is a domain specific language for Python
(read as &quot;crazy getitem/call overloading abuse&quot;) that Donovan and I
schemed up at PyCon 2003 at this super ninja Python/C++ programmer's
(David Abrahams) hotel room. Donovan later inflicted this upon the
masses in Nevow. Check out the Divmod project page for some
examples: <a class="reference" href="http://nevow.com/Nevow2004Tutorial.html">http://nevow.com/Nevow2004Tutorial.html</a></td></tr>
</tbody>
</table>
</div>
<div class="section">
<h1><a id="authors" name="authors">Authors</a></h1>
<ul class="simple">
<li>Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;</li>
</ul>
</div>
<div class="section">
<h1><a id="copyright" name="copyright">Copyright</a></h1>
<p>Copyright 2005 Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;. This program is dual-licensed
free software; you can redistribute it and/or modify it under the terms of the
<a class="reference" href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or the <a class="reference" href="http://www.opensource.org/licenses/afl-2.1.php">Academic Free License v2.1</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -1,125 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
<title>MochiKit.DateTime - &quot;what time is it anyway?&quot;</title>
<link rel="stylesheet" href="../../../include/css/documentation.css" type="text/css" />
<script type="text/javascript" src="../../../packed/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="../../js/toc.js"></script>
</head>
<body>
<div class="document">
<div class="section">
<h1><a id="name" name="name">Name</a></h1>
<p>MochiKit.DateTime - &quot;what time is it anyway?&quot;</p>
</div>
<div class="section">
<h1><a id="synopsis" name="synopsis">Synopsis</a></h1>
<pre class="literal-block">
stringDate = toISOTimestamp(new Date());
dateObject = isoTimestamp(stringDate);
</pre>
</div>
<div class="section">
<h1><a id="description" name="description">Description</a></h1>
<p>Remote servers don't give you JavaScript Date objects, and they certainly
don't want them from you, so you need to deal with string representations
of dates and timestamps. MochiKit.Date does that.</p>
</div>
<div class="section">
<h1><a id="dependencies" name="dependencies">Dependencies</a></h1>
<p>None.</p>
</div>
<div class="section">
<h1><a id="api-reference" name="api-reference">API Reference</a></h1>
<div class="section">
<h2><a id="functions" name="functions">Functions</a></h2>
<p>
<a name="fn-isodate"></a>
<a class="mochidef reference" href="#fn-isodate">isoDate(str)</a>:</p>
<blockquote>
Convert an ISO 8601 date (YYYY-MM-DD) to a <tt class="docutils literal"><span class="pre">Date</span></tt> object.</blockquote>
<p>
<a name="fn-isotimestamp"></a>
<a class="mochidef reference" href="#fn-isotimestamp">isoTimestamp(str)</a>:</p>
<blockquote>
<p>Convert any ISO 8601 <a class="footnote-reference" href="#id3" id="id1" name="id1">[1]</a> timestamp (or something reasonably close to it)
to a <tt class="docutils literal"><span class="pre">Date</span></tt> object. Will accept the &quot;de facto&quot; form:</p>
<blockquote>
YYYY-MM-DD hh:mm:ss</blockquote>
<p>or (the proper form):</p>
<blockquote>
YYYY-MM-DDThh:mm:ssZ</blockquote>
<p>If a time zone designator (&quot;Z&quot; or &quot;[+-]HH:MM&quot;) is not present, then the
local timezone is used.</p>
</blockquote>
<p>
<a name="fn-toisotime"></a>
<a class="mochidef reference" href="#fn-toisotime">toISOTime(date)</a>:</p>
<blockquote>
Convert a <tt class="docutils literal"><span class="pre">Date</span></tt> object to a string in the form of hh:mm:ss</blockquote>
<p>
<a name="fn-toisotimestamp"></a>
<a class="mochidef reference" href="#fn-toisotimestamp">toISOTimestamp(date, realISO=false)</a>:</p>
<blockquote>
<p>Convert a <tt class="docutils literal"><span class="pre">Date</span></tt> object to something that's ALMOST but not quite an
ISO 8601 [1]_timestamp. If it was a proper ISO timestamp it would be:</p>
<blockquote>
YYYY-MM-DDThh:mm:ssZ</blockquote>
<p>However, we see junk in SQL and other places that looks like this:</p>
<blockquote>
YYYY-MM-DD hh:mm:ss</blockquote>
<p>So, this function returns the latter form, despite its name, unless
you pass <tt class="docutils literal"><span class="pre">true</span></tt> for <tt class="docutils literal"><span class="pre">realISO</span></tt>.</p>
</blockquote>
<p>
<a name="fn-toisodate"></a>
<a class="mochidef reference" href="#fn-toisodate">toISODate(date)</a>:</p>
<blockquote>
Convert a <tt class="docutils literal"><span class="pre">Date</span></tt> object to an ISO 8601 <a class="footnote-reference" href="#id3" id="id2" name="id2">[1]</a> date string (YYYY-MM-DD)</blockquote>
<p>
<a name="fn-americandate"></a>
<a class="mochidef reference" href="#fn-americandate">americanDate(str)</a>:</p>
<blockquote>
Converts a MM/DD/YYYY date to a <tt class="docutils literal"><span class="pre">Date</span></tt> object</blockquote>
<p>
<a name="fn-topaddedamericandate"></a>
<a class="mochidef reference" href="#fn-topaddedamericandate">toPaddedAmericanDate(date)</a>:</p>
<blockquote>
Converts a <tt class="docutils literal"><span class="pre">Date</span></tt> object to an MM/DD/YYYY date, e.g. 01/01/2001</blockquote>
<p>
<a name="fn-toamericandate"></a>
<a class="mochidef reference" href="#fn-toamericandate">toAmericanDate(date)</a>:</p>
<blockquote>
Converts a <tt class="docutils literal"><span class="pre">Date</span></tt> object to an M/D/YYYY date, e.g. 1/1/2001</blockquote>
</div>
</div>
<div class="section">
<h1><a id="see-also" name="see-also">See Also</a></h1>
<table class="docutils footnote" frame="void" id="id3" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a name="id3">[1]</a></td><td><em>(<a class="fn-backref" href="#id1">1</a>, <a class="fn-backref" href="#id2">2</a>)</em> W3C profile of ISO 8601: <a class="reference" href="http://www.w3.org/TR/NOTE-datetime">http://www.w3.org/TR/NOTE-datetime</a></td></tr>
</tbody>
</table>
</div>
<div class="section">
<h1><a id="authors" name="authors">Authors</a></h1>
<ul class="simple">
<li>Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;</li>
</ul>
</div>
<div class="section">
<h1><a id="copyright" name="copyright">Copyright</a></h1>
<p>Copyright 2005 Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;. This program is dual-licensed
free software; you can redistribute it and/or modify it under the terms of the
<a class="reference" href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or the <a class="reference" href="http://www.opensource.org/licenses/afl-2.1.php">Academic Free License v2.1</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -1,241 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
<title>MochiKit.Format - string formatting goes here</title>
<link rel="stylesheet" href="../../../include/css/documentation.css" type="text/css" />
<script type="text/javascript" src="../../../packed/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="../../js/toc.js"></script>
</head>
<body>
<div class="document">
<div class="section">
<h1><a id="name" name="name">Name</a></h1>
<p>MochiKit.Format - string formatting goes here</p>
</div>
<div class="section">
<h1><a id="synopsis" name="synopsis">Synopsis</a></h1>
<pre class="literal-block">
assert( truncToFixed(0.12345, 4) == &quot;0.1234&quot; );
assert( roundToFixed(0.12345, 4) == &quot;0.1235&quot; );
assert( twoDigitAverage(1, 0) == &quot;0&quot; );
assert( twoDigitFloat(1.2345) == &quot;1.23&quot; );
assert( twoDigitFloat(1) == &quot;1&quot; );
assert( percentFormat(1.234567) == &quot;123.46%&quot; );
assert( numberFormatter(&quot;###,###%&quot;)(125) == &quot;12,500%&quot; );
assert( numberFormatter(&quot;##.000&quot;)(1.25) == &quot;1.250&quot; );
</pre>
</div>
<div class="section">
<h1><a id="description" name="description">Description</a></h1>
<p>Formatting strings and stringifying numbers is boring, so a couple useful
functions in that domain live here.</p>
</div>
<div class="section">
<h1><a id="dependencies" name="dependencies">Dependencies</a></h1>
<p>None.</p>
</div>
<div class="section">
<h1><a id="overview" name="overview">Overview</a></h1>
<div class="section">
<h2><a id="formatting-numbers" name="formatting-numbers">Formatting Numbers</a></h2>
<p>MochiKit provides an extensible number formatting facility, modeled loosely
after the Number Format Pattern Syntax <a class="footnote-reference" href="#id2" id="id1" name="id1">[1]</a> from Java.
<a class="mochiref reference" href="#fn-numberformatter">numberFormatter(pattern[, placeholder=&quot;&quot;[, locale=&quot;default&quot;])</a>
returns a function that converts Number to string using the given information.
<tt class="docutils literal"><span class="pre">pattern</span></tt> is a string consisting of the following symbols:</p>
<table border="1" class="docutils">
<colgroup>
<col width="15%" />
<col width="85%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Symbol</th>
<th class="head">Meaning</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><tt class="docutils literal"><span class="pre">-</span></tt></td>
<td>If given, used as the position of the minus sign
for negative numbers. If not given, the position
to the left of the first number placeholder is used.</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">#</span></tt></td>
<td>The placeholder for a number that does not imply zero
padding.</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">0</span></tt></td>
<td>The placeholder for a number that implies zero padding.
If it is used to the right of a decimal separator, it
implies trailing zeros, otherwise leading zeros.</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">,</span></tt></td>
<td>The placeholder for a &quot;thousands separator&quot;. May be used
at most once, and it must be to the left of a decimal
separator. Will be replaced by <tt class="docutils literal"><span class="pre">locale.separator</span></tt> in the
result (the default is also <tt class="docutils literal"><span class="pre">,</span></tt>).</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">.</span></tt></td>
<td>The decimal separator. The quantity of <tt class="docutils literal"><span class="pre">#</span></tt> or <tt class="docutils literal"><span class="pre">0</span></tt>
after the decimal separator will determine the precision of
the result. If no decimal separator is present, the
fractional precision is <tt class="docutils literal"><span class="pre">0</span></tt> -- meaning that it will be
rounded to the nearest integer.</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">%</span></tt></td>
<td>If present, the number will be multiplied by <tt class="docutils literal"><span class="pre">100</span></tt> and
the <tt class="docutils literal"><span class="pre">%</span></tt> will be replaced by <tt class="docutils literal"><span class="pre">locale.percent</span></tt>.</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="section">
<h1><a id="api-reference" name="api-reference">API Reference</a></h1>
<div class="section">
<h2><a id="functions" name="functions">Functions</a></h2>
<p>
<a name="fn-formatlocale"></a>
<a class="mochidef reference" href="#fn-formatlocale">formatLocale(locale=&quot;default&quot;)</a>:</p>
<blockquote>
Return a locale object for the given locale. <tt class="docutils literal"><span class="pre">locale</span></tt> may be either a
string, which is looked up in the <tt class="docutils literal"><span class="pre">MochiKit.Format.LOCALE</span></tt> object, or
a locale object. If no locale is given, <tt class="docutils literal"><span class="pre">LOCALE.default</span></tt> is used
(equivalent to <tt class="docutils literal"><span class="pre">LOCALE.en_US</span></tt>).</blockquote>
<p>
<a name="fn-lstrip"></a>
<a class="mochidef reference" href="#fn-lstrip">lstrip(str, chars=&quot;\s&quot;)</a>:</p>
<blockquote>
<p>Returns a string based on <tt class="docutils literal"><span class="pre">str</span></tt> with leading whitespace stripped.</p>
<p>If <tt class="docutils literal"><span class="pre">chars</span></tt> is given, then that expression will be used instead of
whitespace. <tt class="docutils literal"><span class="pre">chars</span></tt> should be a string suitable for use in a <tt class="docutils literal"><span class="pre">RegExp</span></tt>
<tt class="docutils literal"><span class="pre">[character</span> <span class="pre">set]</span></tt>.</p>
</blockquote>
<p>
<a name="fn-numberformatter"></a>
<a class="mochidef reference" href="#fn-numberformatter">numberFormatter(pattern, placeholder=&quot;&quot;, locale=&quot;default&quot;)</a>:</p>
<blockquote>
<p>Return a function <tt class="docutils literal"><span class="pre">formatNumber(aNumber)</span></tt> that formats numbers
as a string according to the given pattern, placeholder and locale.</p>
<p><tt class="docutils literal"><span class="pre">pattern</span></tt> is a string that describes how the numbers should be formatted,
for more information see <a class="reference" href="#formatting-numbers">Formatting Numbers</a>.</p>
<p><tt class="docutils literal"><span class="pre">locale</span></tt> is a string of a known locale (en_US, de_DE, fr_FR, default) or
an object with the following fields:</p>
<table border="1" class="docutils">
<colgroup>
<col width="16%" />
<col width="84%" />
</colgroup>
<tbody valign="top">
<tr><td>separator</td>
<td>The &quot;thousands&quot; separator for this locale (en_US is &quot;,&quot;)</td>
</tr>
<tr><td>decimal</td>
<td>The decimal separator for this locale (en_US is &quot;.&quot;)</td>
</tr>
<tr><td>percent</td>
<td>The percent symbol for this locale (en_US is &quot;%&quot;)</td>
</tr>
</tbody>
</table>
</blockquote>
<p>
<a name="fn-percentformat"></a>
<a class="mochidef reference" href="#fn-percentformat">percentFormat(someFloat)</a>:</p>
<blockquote>
<p>Roughly equivalent to: <tt class="docutils literal"><span class="pre">sprintf(&quot;%.2f%%&quot;,</span> <span class="pre">someFloat</span> <span class="pre">*</span> <span class="pre">100)</span></tt></p>
<p>In new code, you probably want to use:
<a class="mochiref reference" href="#fn-numberformatter">numberFormatter(&quot;#.##%&quot;)(someFloat)</a> instead.</p>
</blockquote>
<p>
<a name="fn-roundtofixed"></a>
<a class="mochidef reference" href="#fn-roundtofixed">roundToFixed(aNumber, precision)</a>:</p>
<blockquote>
<p>Return a string representation of <tt class="docutils literal"><span class="pre">aNumber</span></tt>, rounded to <tt class="docutils literal"><span class="pre">precision</span></tt>
digits with trailing zeros. This is similar to
<tt class="docutils literal"><span class="pre">Number.toFixed(aNumber,</span> <span class="pre">precision)</span></tt>, but this has implementation
consistent rounding behavior (some versions of Safari round 0.5 down!)
and also includes preceding <tt class="docutils literal"><span class="pre">0</span></tt> for numbers less than <tt class="docutils literal"><span class="pre">1</span></tt> (Safari,
again).</p>
<p>For example, <a class="mochiref reference" href="#fn-roundtofixed">roundToFixed(0.1357, 2)</a> returns <tt class="docutils literal"><span class="pre">0.14</span></tt> on every
supported platform, where some return <tt class="docutils literal"><span class="pre">.13</span></tt> for <tt class="docutils literal"><span class="pre">(0.1357).toFixed(2)</span></tt>.</p>
</blockquote>
<p>
<a name="fn-rstrip"></a>
<a class="mochidef reference" href="#fn-rstrip">rstrip(str, chars=&quot;\s&quot;)</a>:</p>
<blockquote>
<p>Returns a string based on <tt class="docutils literal"><span class="pre">str</span></tt> with trailing whitespace stripped.</p>
<p>If <tt class="docutils literal"><span class="pre">chars</span></tt> is given, then that expression will be used instead of
whitespace. <tt class="docutils literal"><span class="pre">chars</span></tt> should be a string suitable for use in a <tt class="docutils literal"><span class="pre">RegExp</span></tt>
<tt class="docutils literal"><span class="pre">[character</span> <span class="pre">set]</span></tt>.</p>
</blockquote>
<p>
<a name="fn-strip"></a>
<a class="mochidef reference" href="#fn-strip">strip(str, chars=&quot;\s&quot;)</a>:</p>
<blockquote>
<p>Returns a string based on <tt class="docutils literal"><span class="pre">str</span></tt> with leading and trailing whitespace
stripped (equivalent to <a class="mochiref reference" href="#fn-lstrip">lstrip(rstrip(str, chars), chars)</a>).</p>
<p>If <tt class="docutils literal"><span class="pre">chars</span></tt> is given, then that expression will be used instead of
whitespace. <tt class="docutils literal"><span class="pre">chars</span></tt> should be a string suitable for use in a <tt class="docutils literal"><span class="pre">RegExp</span></tt>
<tt class="docutils literal"><span class="pre">[character</span> <span class="pre">set]</span></tt>.</p>
</blockquote>
<p>
<a name="fn-trunctofixed"></a>
<a class="mochidef reference" href="#fn-trunctofixed">truncToFixed(aNumber, precision)</a>:</p>
<blockquote>
<p>Return a string representation of <tt class="docutils literal"><span class="pre">aNumber</span></tt>, truncated to <tt class="docutils literal"><span class="pre">precision</span></tt>
digits with trailing zeros. This is similar to
<tt class="docutils literal"><span class="pre">aNumber.toFixed(precision)</span></tt>, but this truncates rather than rounds and
has implementation consistent behavior for numbers less than 1.
Specifically, <a class="mochiref reference" href="#fn-trunctofixed">truncToFixed(aNumber, precision)</a> will always have a
preceding <tt class="docutils literal"><span class="pre">0</span></tt> for numbers less than <tt class="docutils literal"><span class="pre">1</span></tt>.</p>
<p>For example, <a class="mochiref reference" href="#fn-tofixed">toFixed(0.1357, 2)</a> returns <tt class="docutils literal"><span class="pre">0.13</span></tt>.</p>
</blockquote>
<p>
<a name="fn-twodigitaverage"></a>
<a class="mochidef reference" href="#fn-twodigitaverage">twoDigitAverage(numerator, denominator)</a>:</p>
<blockquote>
<p>Calculate an average from a numerator and a denominator and return
it as a string with two digits of precision (e.g. &quot;1.23&quot;).</p>
<p>If the denominator is 0, &quot;0&quot; will be returned instead of <tt class="docutils literal"><span class="pre">NaN</span></tt>.</p>
</blockquote>
<p>
<a name="fn-twodigitfloat"></a>
<a class="mochidef reference" href="#fn-twodigitfloat">twoDigitFloat(someFloat)</a>:</p>
<blockquote>
<p>Roughly equivalent to: <tt class="docutils literal"><span class="pre">sprintf(&quot;%.2f&quot;,</span> <span class="pre">someFloat)</span></tt></p>
<p>In new code, you probably want to use
<a class="mochiref reference" href="#fn-numberformatter">numberFormatter(&quot;#.##&quot;)(someFloat)</a> instead.</p>
</blockquote>
</div>
</div>
<div class="section">
<h1><a id="see-also" name="see-also">See Also</a></h1>
<table class="docutils footnote" frame="void" id="id2" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>Java Number Format Pattern Syntax:
<a class="reference" href="http://java.sun.com/docs/books/tutorial/i18n/format/numberpattern.html">http://java.sun.com/docs/books/tutorial/i18n/format/numberpattern.html</a></td></tr>
</tbody>
</table>
</div>
<div class="section">
<h1><a id="authors" name="authors">Authors</a></h1>
<ul class="simple">
<li>Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;</li>
</ul>
</div>
<div class="section">
<h1><a id="copyright" name="copyright">Copyright</a></h1>
<p>Copyright 2005 Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;. This program is dual-licensed
free software; you can redistribute it and/or modify it under the terms of the
<a class="reference" href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or the <a class="reference" href="http://www.opensource.org/licenses/afl-2.1.php">Academic Free License v2.1</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -1,372 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
<title>MochiKit.Iter - itertools for JavaScript; iteration made HARD, and then easy</title>
<link rel="stylesheet" href="../../../include/css/documentation.css" type="text/css" />
<script type="text/javascript" src="../../../packed/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="../../js/toc.js"></script>
</head>
<body>
<div class="document">
<div class="section">
<h1><a id="name" name="name">Name</a></h1>
<p>MochiKit.Iter - itertools for JavaScript; iteration made HARD, and then easy</p>
</div>
<div class="section">
<h1><a id="synopsis" name="synopsis">Synopsis</a></h1>
<pre class="literal-block">
theSum = sum(takewhile(
partial(operator.gt, 10),
imap(
partial(operator.mul, 2),
count()
)
)
));
assert( theSum == (0 + 0 + 2 + 4 + 6 + 8) );
</pre>
</div>
<div class="section">
<h1><a id="description" name="description">Description</a></h1>
<p>All of the functional programming missing from <a class="mochiref reference" href="Base.html">MochiKit.Base</a> lives
here. The functionality in this module is largely inspired by Python's iteration
protocol <a class="footnote-reference" href="#id4" id="id1" name="id1">[1]</a>, and the itertools module <a class="footnote-reference" href="#id5" id="id2" name="id2">[2]</a>.</p>
<p>MochiKit.Iter defines a standard way to iterate over anything, that you can
extend with <a class="mochiref reference" href="#fn-registeriterator">registerIterator</a>, or by implementing the <tt class="docutils literal"><span class="pre">.iter()</span></tt>
protocol. Iterators are lazy, so it can potentially be cheaper to build a
filter chain of iterators than to build lots of intermediate arrays.
Especially when the data set is very large, but the result is not.</p>
</div>
<div class="section">
<h1><a id="dependencies" name="dependencies">Dependencies</a></h1>
<ul class="simple">
<li><a class="mochiref reference" href="Base.html">MochiKit.Base</a></li>
</ul>
</div>
<div class="section">
<h1><a id="overview" name="overview">Overview</a></h1>
<div class="section">
<h2><a id="iteration-for-javascript" name="iteration-for-javascript">Iteration for JavaScript</a></h2>
<p>The best overview right now is in my Iteration for JavaScript <a class="footnote-reference" href="#id6" id="id3" name="id3">[3]</a> blog entry.
This information will migrate here eventually.</p>
</div>
</div>
<div class="section">
<h1><a id="api-reference" name="api-reference">API Reference</a></h1>
<div class="section">
<h2><a id="errors" name="errors">Errors</a></h2>
<p>
<a name="fn-stopiteration"></a>
<a class="mochidef reference" href="#fn-stopiteration">StopIteration</a>:</p>
<blockquote>
The singleton <a class="mochiref reference" href="Base.html#fn-namederror">MochiKit.Base.NamedError</a> that signifies the end
of an iterator</blockquote>
</div>
<div class="section">
<h2><a id="functions" name="functions">Functions</a></h2>
<p>
<a name="fn-applymap"></a>
<a class="mochidef reference" href="#fn-applymap">applymap(fun, seq[, self])</a>:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">applymap(fun,</span> <span class="pre">seq)</span></tt> --&gt;</dt>
<dd>fun.apply(self, seq0), fun.apply(self, seq1), ...</dd>
</dl>
</blockquote>
<p>
<a name="fn-chain"></a>
<a class="mochidef reference" href="#fn-chain">chain(p, q[, ...])</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">chain(p,</span> <span class="pre">q,</span> <span class="pre">...)</span></tt> --&gt; p0, p1, ... plast, q0, q1, ...</blockquote>
<p>
<a name="fn-count"></a>
<a class="mochidef reference" href="#fn-count">count(n=0)</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">count(n=0)</span></tt> --&gt; n, n + 1, n + 2, ...</blockquote>
<p>
<a name="fn-cycle"></a>
<a class="mochidef reference" href="#fn-cycle">cycle(p)</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">cycle(p)</span></tt> --&gt; p0, p1, ... plast, p0, p1, ...</blockquote>
<p>
<a name="fn-dropwhile"></a>
<a class="mochidef reference" href="#fn-dropwhile">dropwhile(pred, seq)</a>:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">dropwhile(pred,</span> <span class="pre">seq)</span></tt> --&gt; seq[n], seq[n + 1], starting when</dt>
<dd>pred(seq[n]) fails</dd>
</dl>
</blockquote>
<p>
<a name="fn-every"></a>
<a class="mochidef reference" href="#fn-every">every(iterable, func)</a>:</p>
<blockquote>
Return <tt class="docutils literal"><span class="pre">true</span></tt> if <tt class="docutils literal"><span class="pre">func(item)</span></tt> is <tt class="docutils literal"><span class="pre">true</span></tt> for every item in
<tt class="docutils literal"><span class="pre">iterable</span></tt>.</blockquote>
<p>
<a name="fn-exhaust"></a>
<a class="mochidef reference" href="#fn-exhaust">exhaust(iterable)</a>:</p>
<blockquote>
Exhausts an iterable without saving the results anywhere,
like <a class="mochiref reference" href="#fn-list">list(iterable)</a> when you don't care what the output is.</blockquote>
<p>
<a name="fn-foreach"></a>
<a class="mochidef reference" href="#fn-foreach">forEach(iterable, func[, self])</a>:</p>
<blockquote>
Call <tt class="docutils literal"><span class="pre">func</span></tt> for each item in <tt class="docutils literal"><span class="pre">iterable</span></tt>, and don't save the results.</blockquote>
<p>
<a name="fn-groupby"></a>
<a class="mochidef reference" href="#fn-groupby">groupby(iterable[, keyfunc])</a>:</p>
<blockquote>
<p>Make an iterator that returns consecutive keys and groups from the
iterable. The key is a function computing a key value for each element.
If not specified or is None, key defaults to an identity function and
returns the element unchanged. Generally, the iterable needs to already be
sorted on the same key function.</p>
<p>The returned group is itself an iterator that shares the underlying
iterable with <a class="mochiref reference" href="#fn-groupby">groupby()</a>. Because the source is shared, when the
groupby object is advanced, the previous group is no longer visible.
So, if that data is needed later, it should be stored as an array:</p>
<pre class="literal-block">
var groups = [];
var uniquekeys = [];
forEach(groupby(data, keyfunc), function (key_group) {
groups.push(list(key_group[1]));
uniquekeys.push(key_group[0]);
});
</pre>
<p>As a convenience, <a class="mochiref reference" href="#fn-groupby_as_array">groupby_as_array()</a> is provided to suit the above
use case.</p>
</blockquote>
<p>
<a name="fn-groupby_as_array"></a>
<a class="mochidef reference" href="#fn-groupby_as_array">groupby_as_array(iterable[, keyfunc])</a>:</p>
<blockquote>
Perform the same task as <a class="mochiref reference" href="#fn-groupby">groupby()</a>, except return an array of
arrays instead of an iterator of iterators.</blockquote>
<p>
<a name="fn-iextend"></a>
<a class="mochidef reference" href="#fn-iextend">iextend(lst, iterable)</a>:</p>
<blockquote>
Just like <a class="mochiref reference" href="#fn-list">list(iterable)</a>, except it pushes results on <tt class="docutils literal"><span class="pre">lst</span></tt>
rather than creating a new one.</blockquote>
<p>
<a name="fn-ifilter"></a>
<a class="mochidef reference" href="#fn-ifilter">ifilter(pred, seq)</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">ifilter(pred,</span> <span class="pre">seq)</span></tt> --&gt; elements of seq where <tt class="docutils literal"><span class="pre">pred(elem)</span></tt> is <tt class="docutils literal"><span class="pre">true</span></tt></blockquote>
<p>
<a name="fn-ifilterfalse"></a>
<a class="mochidef reference" href="#fn-ifilterfalse">ifilterfalse(pred, seq)</a>:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">ifilterfalse(pred,</span> <span class="pre">seq)</span></tt> --&gt; elements of seq where <tt class="docutils literal"><span class="pre">pred(elem)</span></tt> is</dt>
<dd><tt class="docutils literal"><span class="pre">false</span></tt></dd>
</dl>
</blockquote>
<p>
<a name="fn-imap"></a>
<a class="mochidef reference" href="#fn-imap">imap(fun, p, q[, ...])</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">imap(fun,</span> <span class="pre">p,</span> <span class="pre">q,</span> <span class="pre">...)</span></tt> --&gt; fun(p0, q0, ...), fun(p1, q1, ...), ...</blockquote>
<p>
<a name="fn-islice"></a>
<a class="mochidef reference" href="#fn-islice">islice(seq, [start,] stop[, step])</a>:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">islice(seq,</span> <span class="pre">[start,]</span> <span class="pre">stop[,</span> <span class="pre">step])</span></tt> --&gt; elements from</dt>
<dd>seq[start:stop:step] (in Python slice syntax)</dd>
</dl>
</blockquote>
<p>
<a name="fn-iter"></a>
<a class="mochidef reference" href="#fn-iter">iter(iterable[, sentinel])</a>:</p>
<blockquote>
<p>Convert the given argument to an iterator (object implementing
<tt class="docutils literal"><span class="pre">.next()</span></tt>).</p>
<ol class="arabic simple">
<li>If <tt class="docutils literal"><span class="pre">iterable</span></tt> is an iterator (implements <tt class="docutils literal"><span class="pre">.next()</span></tt>), then it will
be returned as-is.</li>
<li>If <tt class="docutils literal"><span class="pre">iterable</span></tt> is an iterator factory (implements <tt class="docutils literal"><span class="pre">.iter()</span></tt>), then
the result of <tt class="docutils literal"><span class="pre">iterable.iter()</span></tt> will be returned.</li>
<li>Otherwise, the iterator factory <a class="mochiref reference" href="Base.html#fn-adapterregistry">MochiKit.Base.AdapterRegistry</a>
is used to find a match.</li>
<li>If no factory is found, it will throw <tt class="docutils literal"><span class="pre">TypeError</span></tt></li>
</ol>
<p>Built-in iterator factories are present for Array-like objects, and
objects that implement the <tt class="docutils literal"><span class="pre">iterateNext</span></tt> protocol (e.g. the result of
Mozilla's <tt class="docutils literal"><span class="pre">document.evaluate</span></tt>).</p>
<p>When used directly, using an iterator should look like this:</p>
<pre class="literal-block">
var it = iter(iterable);
try {
while (var o = it.next()) {
// use o
}
} catch (e) {
if (e != StopIteration) {
throw e;
}
// pass
}
</pre>
<p>This is ugly, so you should use the higher order functions to work
with iterators whenever possible.</p>
</blockquote>
<p>
<a name="fn-izip"></a>
<a class="mochidef reference" href="#fn-izip">izip(p, q[, ...])</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">izip(p,</span> <span class="pre">q,</span> <span class="pre">...)</span></tt> --&gt; [p0, q0, ...], [p1, q1, ...], ...</blockquote>
<p>
<a name="fn-list"></a>
<a class="mochidef reference" href="#fn-list">list(iterable)</a>:</p>
<blockquote>
Convert <tt class="docutils literal"><span class="pre">iterable</span></tt> to a new <tt class="docutils literal"><span class="pre">Array</span></tt></blockquote>
<p>
<a name="fn-next"></a>
<a class="mochidef reference" href="#fn-next">next(iterator)</a>:</p>
<blockquote>
Return <tt class="docutils literal"><span class="pre">iterator.next()</span></tt></blockquote>
<p>
<a name="fn-range"></a>
<a class="mochidef reference" href="#fn-range">range([start,] stop[, step])</a>:</p>
<blockquote>
<p>Return an iterator containing an arithmetic progression of integers.</p>
<p><tt class="docutils literal"><span class="pre">range(i,</span> <span class="pre">j)</span></tt> returns <a class="mochiref reference" href="#fn-iter">iter([i, i + 1, i + 2, ..., j - 1])</a></p>
<p><tt class="docutils literal"><span class="pre">start</span></tt> (!) defaults to <tt class="docutils literal"><span class="pre">0</span></tt>. When <tt class="docutils literal"><span class="pre">step</span></tt> is given, it specifies the
increment (or decrement). The end point is omitted!</p>
<p>For example, <tt class="docutils literal"><span class="pre">range(4)</span></tt> returns <a class="mochiref reference" href="#fn-iter">iter([0, 1, 2, 3])</a>.
This iterates over exactly the valid indexes for an array of 4 elements.</p>
</blockquote>
<p>
<a name="fn-reduce"></a>
<a class="mochidef reference" href="#fn-reduce">reduce(fn, iterable[, initial])</a>:</p>
<blockquote>
<p>Apply <tt class="docutils literal"><span class="pre">fn(a,</span> <span class="pre">b)</span></tt> cumulatively to the items of an
iterable from left to right, so as to reduce the iterable
to a single value.</p>
<p>For example:</p>
<pre class="literal-block">
reduce(function (a, b) { return x + y; }, [1, 2, 3, 4, 5])
</pre>
<p>calculates:</p>
<pre class="literal-block">
((((1 + 2) + 3) + 4) + 5).
</pre>
<p>If initial is given, it is placed before the items of the sequence
in the calculation, and serves as a default when the sequence is
empty.</p>
<p>Note that the above example could be written more clearly as:</p>
<pre class="literal-block">
reduce(operator.add, [1, 2, 3, 4, 5])
</pre>
<p>Or even simpler:</p>
<pre class="literal-block">
sum([1, 2, 3, 4, 5])
</pre>
</blockquote>
<p>
<a name="fn-registeriteratorfactory"></a>
<a class="mochidef reference" href="#fn-registeriteratorfactory">registerIteratorFactory(name, check, iterfactory[, override])</a>:</p>
<blockquote>
<p>Register an iterator factory for use with the iter function.</p>
<p><tt class="docutils literal"><span class="pre">check</span></tt> is a <tt class="docutils literal"><span class="pre">function(a)</span></tt> that returns <tt class="docutils literal"><span class="pre">true</span></tt> if <tt class="docutils literal"><span class="pre">a</span></tt> can be
converted into an iterator with <tt class="docutils literal"><span class="pre">iterfactory</span></tt>.</p>
<p><tt class="docutils literal"><span class="pre">iterfactory</span></tt> is a <tt class="docutils literal"><span class="pre">function(a)</span></tt> that returns an object with a
<tt class="docutils literal"><span class="pre">.next()</span></tt> method that returns the next value in the sequence.</p>
<p><tt class="docutils literal"><span class="pre">iterfactory</span></tt> is guaranteed to only be called if <tt class="docutils literal"><span class="pre">check(a)</span></tt>
returns a true value.</p>
<p>If <tt class="docutils literal"><span class="pre">override</span></tt> is <tt class="docutils literal"><span class="pre">true</span></tt>, then it will be made the
highest precedence iterator factory. Otherwise, the lowest.</p>
</blockquote>
<p>
<a name="fn-repeat"></a>
<a class="mochidef reference" href="#fn-repeat">repeat(elem[, n])</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">repeat(elem,</span> <span class="pre">[,n])</span></tt> --&gt; elem, elem, elem, ... endlessly or up to n times</blockquote>
<p>
<a name="fn-reversed"></a>
<a class="mochidef reference" href="#fn-reversed">reversed(iterable)</a>:</p>
<blockquote>
Return a reversed array from iterable.</blockquote>
<p>
<a name="fn-some"></a>
<a class="mochidef reference" href="#fn-some">some(iterable, func)</a>:</p>
<blockquote>
Return <tt class="docutils literal"><span class="pre">true</span></tt> if <tt class="docutils literal"><span class="pre">func(item)</span></tt> is <tt class="docutils literal"><span class="pre">true</span></tt> for at least one item in
<tt class="docutils literal"><span class="pre">iterable</span></tt>.</blockquote>
<p>
<a name="fn-sorted"></a>
<a class="mochidef reference" href="#fn-sorted">sorted(iterable[, cmp])</a>:</p>
<blockquote>
Return a sorted array from iterable.</blockquote>
<p>
<a name="fn-sum"></a>
<a class="mochidef reference" href="#fn-sum">sum(iterable, start=0)</a>:</p>
<blockquote>
<p>Returns the sum of a sequence of numbers plus the value
of parameter <tt class="docutils literal"><span class="pre">start</span></tt> (with a default of 0). When the sequence is
empty, returns start.</p>
<p>Equivalent to:</p>
<pre class="literal-block">
reduce(operator.add, iterable, start);
</pre>
</blockquote>
<p>
<a name="fn-takewhile"></a>
<a class="mochidef reference" href="#fn-takewhile">takewhile(pred, seq)</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">takewhile(pred,</span> <span class="pre">seq)</span></tt> --&gt; seq[0], seq[1], ... until pred(seq[n]) fails</blockquote>
<p>
<a name="fn-tee"></a>
<a class="mochidef reference" href="#fn-tee">tee(iterable, n=2)</a>:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">tee(it,</span> <span class="pre">n=2)</span></tt> --&gt; [it1, it2, it3, ... itn] splits one iterator into n</blockquote>
</div>
</div>
<div class="section">
<h1><a id="see-also" name="see-also">See Also</a></h1>
<table class="docutils footnote" frame="void" id="id4" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1" name="id4">[1]</a></td><td>The iteration protocol is described in
PEP 234 - Iterators: <a class="reference" href="http://www.python.org/peps/pep-0234.html">http://www.python.org/peps/pep-0234.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id5" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id2" name="id5">[2]</a></td><td>Python's itertools
module: <a class="reference" href="http://docs.python.org/lib/module-itertools.html">http://docs.python.org/lib/module-itertools.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id6" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id3" name="id6">[3]</a></td><td>Iteration in JavaScript: <a class="reference" href="http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/">http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/</a></td></tr>
</tbody>
</table>
</div>
<div class="section">
<h1><a id="authors" name="authors">Authors</a></h1>
<ul class="simple">
<li>Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;</li>
</ul>
</div>
<div class="section">
<h1><a id="copyright" name="copyright">Copyright</a></h1>
<p>Copyright 2005 Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;. This program is dual-licensed
free software; you can redistribute it and/or modify it under the terms of the
<a class="reference" href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or the <a class="reference" href="http://www.opensource.org/licenses/afl-2.1.php">Academic Free License v2.1</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -1,310 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
<title>MochiKit.Logging - we're all tired of alert()</title>
<link rel="stylesheet" href="../../../include/css/documentation.css" type="text/css" />
<script type="text/javascript" src="../../../packed/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="../../js/toc.js"></script>
</head>
<body>
<div class="document">
<div class="section">
<h1><a id="name" name="name">Name</a></h1>
<p>MochiKit.Logging - we're all tired of alert()</p>
</div>
<div class="section">
<h1><a id="synopsis" name="synopsis">Synopsis</a></h1>
<pre class="literal-block">
log(&quot;INFO messages are so boring&quot;);
logDebug(&quot;DEBUG messages are even worse&quot;);
log(&quot;good thing I can pass&quot;, objects, &quot;conveniently&quot;);
</pre>
</div>
<div class="section">
<h1><a id="description" name="description">Description</a></h1>
<p>MochiKit.Logging steals some ideas from Python's logging module <a class="footnote-reference" href="#id6" id="id1" name="id1">[1]</a>, but
completely forgot about the Java <a class="footnote-reference" href="#id7" id="id2" name="id2">[2]</a> inspiration. This is a KISS module for
logging that provides enough flexibility to do just about anything via
listeners, but without all the cruft.</p>
</div>
<div class="section">
<h1><a id="dependencies" name="dependencies">Dependencies</a></h1>
<ul class="simple">
<li><a class="mochiref reference" href="Base.html">MochiKit.Base</a></li>
</ul>
</div>
<div class="section">
<h1><a id="overview" name="overview">Overview</a></h1>
<div class="section">
<h2><a id="native-console-logging" name="native-console-logging">Native Console Logging</a></h2>
<p>As of MochiKit 1.3, the default logger will log all messages to your browser's
native console. This is currently supported in Safari, Opera 9, and Firefox
when the <a class="reference" href="http://www.joehewitt.com/software/firebug/">FireBug</a> extension is installed.</p>
<p>To disable this behavior:</p>
<pre class="literal-block">
MochiKit.Logging.logger.useNativeLogging = false;
</pre>
</div>
<div class="section">
<h2><a id="bookmarklet-based-debugging" name="bookmarklet-based-debugging">Bookmarklet Based Debugging</a></h2>
<p>JavaScript is at a serious disadvantage without a standard console for
&quot;print&quot; statements. Everything else has one. The closest thing that
you get in a browser environment is the <tt class="docutils literal"><span class="pre">alert</span></tt> function, which is
absolutely evil.</p>
<p>This leaves you with one reasonable solution: do your logging in the page
somehow. The problem here is that you don't want to clutter the page with
debugging tools. The solution to that problem is what we call BBD, or
Bookmarklet Based Debugging <a class="footnote-reference" href="#id8" id="id4" name="id4">[3]</a>.</p>
<p>Simply create a bookmarklet for <a class="reference" href="javascript:MochiKit.Logging.logger.debuggingBookmarklet()">javascript:MochiKit.Logging.logger.debuggingBookmarklet()</a>,
and whack it whenever you want to see what's in the logger. Of course, this
means you must drink the MochiKit.Logging kool-aid. It's tangy and sweet,
don't worry.</p>
<p>Currently this is an ugly <tt class="docutils literal"><span class="pre">alert</span></tt>, but we'll have something spiffy
Real Soon Now, and when we do, you only have to upgrade MochiKit.Logging,
not your bookmarklet!</p>
</div>
</div>
<div class="section">
<h1><a id="api-reference" name="api-reference">API Reference</a></h1>
<div class="section">
<h2><a id="constructors" name="constructors">Constructors</a></h2>
<p>
<a name="fn-logmessage"></a>
<a class="mochidef reference" href="#fn-logmessage">LogMessage(num, level, info)</a>:</p>
<blockquote>
<p>Properties:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">num</span></tt>:</dt>
<dd>Identifier for the log message</dd>
<dt><tt class="docutils literal"><span class="pre">level</span></tt>:</dt>
<dd>Level of the log message (<tt class="docutils literal"><span class="pre">&quot;INFO&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;WARN&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;DEBUG&quot;</span></tt>,
etc.)</dd>
<dt><tt class="docutils literal"><span class="pre">info</span></tt>:</dt>
<dd>All other arguments passed to log function as an <tt class="docutils literal"><span class="pre">Array</span></tt></dd>
<dt><tt class="docutils literal"><span class="pre">timestamp</span></tt>:</dt>
<dd><tt class="docutils literal"><span class="pre">Date</span></tt> object timestamping the log message</dd>
</dl>
</blockquote>
</blockquote>
<p>
<a name="fn-logger"></a>
<a class="mochidef reference" href="#fn-logger">Logger([maxSize])</a>:</p>
<blockquote>
<p>A basic logger object that has a buffer of recent messages
plus a listener dispatch mechanism for &quot;real-time&quot; logging
of important messages.</p>
<p><tt class="docutils literal"><span class="pre">maxSize</span></tt> is the maximum number of entries in the log.
If <tt class="docutils literal"><span class="pre">maxSize</span> <span class="pre">&gt;=</span> <span class="pre">0</span></tt>, then the log will not buffer more than that
many messages. So if you don't like logging at all, be sure to
pass <tt class="docutils literal"><span class="pre">0</span></tt>.</p>
<p>There is a default logger available named &quot;logger&quot;, and several
of its methods are also global functions:</p>
<blockquote>
<tt class="docutils literal"><span class="pre">logger.log</span></tt> -&gt; <tt class="docutils literal"><span class="pre">log</span></tt>
<tt class="docutils literal"><span class="pre">logger.debug</span></tt> -&gt; <tt class="docutils literal"><span class="pre">logDebug</span></tt>
<tt class="docutils literal"><span class="pre">logger.warning</span></tt> -&gt; <tt class="docutils literal"><span class="pre">logWarning</span></tt>
<tt class="docutils literal"><span class="pre">logger.error</span></tt> -&gt; <tt class="docutils literal"><span class="pre">logError</span></tt>
<tt class="docutils literal"><span class="pre">logger.fatal</span></tt> -&gt; <tt class="docutils literal"><span class="pre">logFatal</span></tt></blockquote>
</blockquote>
<p>
<a name="fn-logger.prototype.addlistener"></a>
<a class="mochidef reference" href="#fn-logger.prototype.addlistener">Logger.prototype.addListener(ident, filter, listener)</a>:</p>
<blockquote>
<p>Add a listener for log messages.</p>
<p><tt class="docutils literal"><span class="pre">ident</span></tt> is a unique identifier that may be used to remove the listener
later on.</p>
<p><tt class="docutils literal"><span class="pre">filter</span></tt> can be one of the following:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">null</span></tt>:</dt>
<dd><tt class="docutils literal"><span class="pre">listener(msg)</span></tt> will be called for every log message
received.</dd>
<dt><tt class="docutils literal"><span class="pre">string</span></tt>:</dt>
<dd><a class="mochiref reference" href="#fn-loglevelatleast">logLevelAtLeast(filter)</a> will be used as the function
(see below).</dd>
<dt><tt class="docutils literal"><span class="pre">function</span></tt>:</dt>
<dd><tt class="docutils literal"><span class="pre">filter(msg)</span></tt> will be called for every msg, if it returns
true then <tt class="docutils literal"><span class="pre">listener(msg)</span></tt> will be called.</dd>
</dl>
</blockquote>
<p><tt class="docutils literal"><span class="pre">listener</span></tt> is a function that takes one argument, a log message. A log
message is an object (<a class="mochiref reference" href="#fn-logmessage">LogMessage</a> instance) that has at least these
properties:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">num</span></tt>:</dt>
<dd>A counter that uniquely identifies a log message (per-logger)</dd>
<dt><tt class="docutils literal"><span class="pre">level</span></tt>:</dt>
<dd>A string or number representing the log level. If string, you
may want to use <tt class="docutils literal"><span class="pre">LogLevel[level]</span></tt> for comparison.</dd>
<dt><tt class="docutils literal"><span class="pre">info</span></tt>:</dt>
<dd>An Array of objects passed as additional arguments to the <tt class="docutils literal"><span class="pre">log</span></tt>
function.</dd>
</dl>
</blockquote>
</blockquote>
<p>
<a name="fn-logger.prototype.baselog"></a>
<a class="mochidef reference" href="#fn-logger.prototype.baselog">Logger.prototype.baseLog(level, message[, ...])</a>:</p>
<blockquote>
<p>The base functionality behind all of the log functions.
The first argument is the log level as a string or number,
and all other arguments are used as the info list.</p>
<p>This function is available partially applied as:</p>
<blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="61%" />
<col width="39%" />
</colgroup>
<tbody valign="top">
<tr><td>Logger.debug</td>
<td>'DEBUG'</td>
</tr>
<tr><td>Logger.log</td>
<td>'INFO'</td>
</tr>
<tr><td>Logger.error</td>
<td>'ERROR'</td>
</tr>
<tr><td>Logger.fatal</td>
<td>'FATAL'</td>
</tr>
<tr><td>Logger.warning</td>
<td>'WARNING'</td>
</tr>
</tbody>
</table>
</blockquote>
<p>For the default logger, these are also available as global functions,
see the <a class="mochiref reference" href="#fn-logger">Logger</a> constructor documentation for more info.</p>
</blockquote>
<p>
<a name="fn-logger.prototype.clear"></a>
<a class="mochidef reference" href="#fn-logger.prototype.clear">Logger.prototype.clear()</a>:</p>
<blockquote>
Clear all messages from the message buffer.</blockquote>
<p>
<a name="fn-logger.prototype.debuggingbookmarklet"></a>
<a class="mochidef reference" href="#fn-logger.prototype.debuggingbookmarklet">Logger.prototype.debuggingBookmarklet()</a>:</p>
<blockquote>
<p>Display the contents of the logger in a useful way for browsers.</p>
<p>Currently, if <a class="mochiref reference" href="LoggingPane.html">MochiKit.LoggingPane</a> is loaded, then a pop-up
<a class="mochiref reference" href="LoggingPane.html#fn-loggingpane">MochiKit.LoggingPane.LoggingPane</a> will be used. Otherwise,
it will be an alert with <a class="mochiref reference" href="#fn-logger.prototype.getmessagetext">Logger.prototype.getMessageText()</a>.</p>
</blockquote>
<p>
<a name="fn-logger.prototype.dispatchlisteners"></a>
<a class="mochidef reference" href="#fn-logger.prototype.dispatchlisteners">Logger.prototype.dispatchListeners(msg)</a>:</p>
<blockquote>
Dispatch a log message to all listeners.</blockquote>
<p>
<a name="fn-logger.prototype.getmessages"></a>
<a class="mochidef reference" href="#fn-logger.prototype.getmessages">Logger.prototype.getMessages(howMany)</a>:</p>
<blockquote>
Return a list of up to <tt class="docutils literal"><span class="pre">howMany</span></tt> messages from the message buffer.</blockquote>
<p>
<a name="fn-logger.prototype.getmessagetext"></a>
<a class="mochidef reference" href="#fn-logger.prototype.getmessagetext">Logger.prototype.getMessageText(howMany)</a>:</p>
<blockquote>
<p>Get a string representing up to the last <tt class="docutils literal"><span class="pre">howMany</span></tt> messages in the
message buffer. The default is <tt class="docutils literal"><span class="pre">30</span></tt>.</p>
<p>The message looks like this:</p>
<pre class="literal-block">
LAST {messages.length} MESSAGES:
[{msg.num}] {msg.level}: {m.info.join(' ')}
[{msg.num}] {msg.level}: {m.info.join(' ')}
...
</pre>
<p>If you want some other format, use
<a class="mochiref reference" href="#fn-logger.prototype.getmessages">Logger.prototype.getMessages</a> and do it yourself.</p>
</blockquote>
<p>
<a name="fn-logger.prototype.removelistener"></a>
<a class="mochidef reference" href="#fn-logger.prototype.removelistener">Logger.prototype.removeListener(ident)</a>:</p>
<blockquote>
Remove a listener using the ident given to <a class="mochiref reference" href="#fn-logger.prototype.addlistener">Logger.prototype.addListener</a></blockquote>
</div>
<div class="section">
<h2><a id="functions" name="functions">Functions</a></h2>
<p>
<a name="fn-alertlistener"></a>
<a class="mochidef reference" href="#fn-alertlistener">alertListener(msg)</a>:</p>
<blockquote>
Ultra-obnoxious <tt class="docutils literal"><span class="pre">alert(...)</span></tt> listener</blockquote>
<p>
<a name="fn-logdebug"></a>
<a class="mochidef reference" href="#fn-logdebug">logDebug(message[, info[, ...]])</a>:</p>
<blockquote>
Log an INFO message to the default logger</blockquote>
<p>
<a name="fn-logdebug"></a>
<a class="mochidef reference" href="#fn-logdebug">logDebug(message[, info[, ...]])</a>:</p>
<blockquote>
Log a DEBUG message to the default logger</blockquote>
<p>
<a name="fn-logerror"></a>
<a class="mochidef reference" href="#fn-logerror">logError(message[, info[, ...]])</a>:</p>
<blockquote>
Log an ERROR message to the default logger</blockquote>
<p>
<a name="fn-logfatal"></a>
<a class="mochidef reference" href="#fn-logfatal">logFatal(message[, info[, ...]])</a>:</p>
<blockquote>
Log a FATAL message to the default logger</blockquote>
<p>
<a name="fn-loglevelatleast"></a>
<a class="mochidef reference" href="#fn-loglevelatleast">logLevelAtLeast(minLevel)</a>:</p>
<blockquote>
Return a function that will match log messages whose level
is at least minLevel</blockquote>
<p>
<a name="fn-logwarning"></a>
<a class="mochidef reference" href="#fn-logwarning">logWarning(message[, info[, ...]])</a>:</p>
<blockquote>
Log a WARNING message to the default logger</blockquote>
</div>
</div>
<div class="section">
<h1><a id="see-also" name="see-also">See Also</a></h1>
<table class="docutils footnote" frame="void" id="id6" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1" name="id6">[1]</a></td><td>Python's logging module: <a class="reference" href="http://docs.python.org/lib/module-logging.html">http://docs.python.org/lib/module-logging.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id7" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id2" name="id7">[2]</a></td><td>PEP 282, where they admit all of the Java influence: <a class="reference" href="http://www.python.org/peps/pep-0282.html">http://www.python.org/peps/pep-0282.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id8" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id4" name="id8">[3]</a></td><td>Original Bookmarklet Based Debugging blather: <a class="reference" href="http://bob.pythonmac.org/archives/2005/07/03/bookmarklet-based-debugging/">http://bob.pythonmac.org/archives/2005/07/03/bookmarklet-based-debugging/</a></td></tr>
</tbody>
</table>
</div>
<div class="section">
<h1><a id="authors" name="authors">Authors</a></h1>
<ul class="simple">
<li>Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;</li>
</ul>
</div>
<div class="section">
<h1><a id="copyright" name="copyright">Copyright</a></h1>
<p>Copyright 2005 Bob Ippolito &lt;<a class="reference" href="mailto:bob&#64;redivi.com">bob&#64;redivi.com</a>&gt;. This program is dual-licensed
free software; you can redistribute it and/or modify it under the terms of the
<a class="reference" href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or the <a class="reference" href="http://www.opensource.org/licenses/afl-2.1.php">Academic Free License v2.1</a>.</p>
</div>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More