Closed Bug 109739 Opened 23 years ago Closed 22 years ago

plugins registered in appreg each time they are used (?)

Categories

(Core Graveyard :: Plug-ins, defect, P3)

x86
All
defect

Tracking

(Not tracked)

RESOLVED FIXED
mozilla1.0.1

People

(Reporter: spam, Assigned: srgchrpv)

References

Details

(Keywords: perf, topembed, Whiteboard: [ADT2] [needs reviews])

Attachments

(1 file, 12 obsolete files)

on linux i noticed that appreg in a profile created on Oct. 30th had grown to
2.5MB. Did a strings on it and counted how many times it mentioned various plugins:

grep QuickTime appreg.text |wc -l
   1668

grep x-java-applet appreg.text |wc -l
    790

This seems a little much? A new appreg suffices with mentioning these plugins
respectively 12 and 10 times, which is more sane as it reflects various so.files
and their associated file-extentions for mime types.
the file "appreg.text" was one i created by running "strings appreg >appreg.text
(To look at what was there. The content of appreg itself is partly binary.)
reading that file can't be good for startup performance.  :)
Keywords: perf
i don't think it affects startup actually..doesn't seem it's read then
but i think my plugins start quicker when the file is smaller - seems to be far
less disk activity since i regenerated it to a smaller version.
wonder if this may have started accumulating after some old crash.

Been testing plugins all over the place now. Restarted several times: appreg
filesize remains sane. Hard to repro -> lowering severity.

This reminds me of the growing rfd files that could occure.

av: Old huge appreg available if you ever get the time to poke at it.
Severity: normal → minor
This extra size may be caused by caching plugin info found in the initial scan
at startup. Run regexport to dump the registry. In order to increase subsequent
startup time, all found plugins, valid or not, are cached in the registry. They
are only loaded at start time if their time stamp has changed from the registry.
I install nightlies regularly. The plugin dir is wiped clean then, so i copy
over plugins again. I assume this means the appreg increments on each install,
unless i created a new profile each time too. Which I don't - I'm pretty pleased
with the one i have.. So this is the expected behaviour then? If so, please
resolve as invalid.
--- Mass reassigning Unix bugs to serge ---
Assignee: av → serge
I think this is all platforms bug, at least my w2k registry.dat is getting fat 
too, which probably decrease a performance:(
I'm not sure when and how we are doing registry packing, I think it should be at 
installation time.
--> dveditz.

Assignee: serge → dveditz
OS: Linux → All
Back to you... you guys are mis-using the registry format. The interface
promises a lot more than the implementation delivers since we only filled it out
as far as was needed. In particular "RemoveSubtree()" is a huge red flag since
that strands records in the file marked deleted but never recovered.

The registry is slow and inefficient. I strongly urge you to consider some other
data format, like a text file. If you insist on using it then you'll have to
implement the registry packing API and expose it to nsIRegistry. It'll still be
slow, but at least it won't grow.
Assignee: dveditz → serge
>The registry is slow and inefficient. I strongly urge you to consider some 
>other data format, like a text file.
dp, what do you think?
Current code reads and writes to the persistent store (appregistry now) in full
and not in parts. So this would work nicely. But I am not sure about the
priority of this or whether we should do that.

If packing is all we need to do at specific instances, then exposing the pack
api and doing it is simple. The entire reason this application registry exists
to for application to note down persistent data - infrequently changing data.
Plugin information does not change frequently. So this is the right data
storage. It is conceivable there will be more customers of the appregistry.

So I think doing the pack is a better way to go. Or we should rewrite
appregistry's implementation.

Disadvantages of another file:
- Another file open/read/write. Bad for mac.
I don't know much about it, but what about using RDF to store plugin information
and extending mimetypes.rdf for plugin handlers? Does RDF have similar
performance problems as the app registry? I'd be nice to tie in helper
application preferences with plugins.
Using the registry isn't so bad if you change the data structure you're using so
you don't delete and re-write every start. For example, instead of keying off
generic labels like "plugin-1" which change their meaning, key off a filename or
MIME type.

If you need to you can delete things which are gone for good, just don't delete
"just in case" things have gone away.
My understanding of frequency of write in user senario:
- On install
- whenever plugins are added/removed

If we are writing to the registry more often, that is a bug.
--->dougt says he wants this
Assignee: serge → dougt
Attached patch Key plugin by its given name. (obsolete) — Splinter Review
This patch removes the anonymous plugin key and usings a key based on the
plugin's name.	I could not think of an instance where an application of two
plugins of the same name registered in two different locations.  If this
support is required, which I doubt, we could used the plugin's location on disk
as the key.  In either way, we will have a reliable way to verify that the key
in the registry is indeed the plugin we are looking at.

An overview of the patch:

AddPluginInfoToRegistry no longer needs to have the |keyname| passed in as it
will use the |tag->mName|.

Removal of the RemoveSubtree().  If we fail, there may be valid cached entries
in the cache.  We should be more optimistic.

Since we don't need to toss the subtree anymore, I removed
|registry->RemoveSubtree| from CachePluginsInfo().

From the same function, I removed the code which generated the anonymous key.

So, with these changes, I think that we should not be growing the registry
anymore.  Dan, am I missing someting?
Plugin folks, please comment on paragraph (1).
Doug, there could be duplicate plugins in different locations and all must be 
present in plugin info cache, we cache info on all plugins including 'unwanted' 
and duplicate.
okay.  so, we can use the key:

  rv = registry->AddSubtree(top, tag->mFileName, &pluginKey);

In this way, every plugin will be reflected.  The only draw back is that you
will have this as the subtree key AND in utf8 node data.  But that is not nearly
as bad bloat without this patch.  :-).  

Av, with this change, can I get your r=?
Comment on attachment 69581 [details] [diff] [review]
Key plugin by its given name.

r=av given dup plugins are reflected.
Attachment #69581 - Flags: review+
You won't be able to AddSubtree() with a filename, because the filename may have
slashes or even illegal chars.

There is an AddSubtreeRaw, though, if you need it. THen you'll have to change
your enumeration to one that can handle "raw" unprocessed nodes.
-  // We dont want any old plugins that dont exist anymore hanging around
-  // So remove and re-add the root of the plugins info
-  nsresult rv = registry->RemoveSubtree(nsIRegistry::Common, kPluginsRootKey);

I dont understand how this is going to work. Say the user removed a bunch of
plugins, then this will cause the removed plugins to still exist in our registry.

The reason we did "plugin-0" for the keyname was 'cause of restriction on what
the key can have. (See Dan's comment and Dan stop causing collisions ;-)

Overall, I am confused at what problem this patch is solving.
Comment on attachment 69581 [details] [diff] [review]
Key plugin by its given name.

>+  // key by the plugin name.
>+  rv = registry->AddSubtree(top, tag->mName, &pluginKey);

This will surely break. For one thing the narrow char apis are UTF8,
and will fail if given invalid UTF8 data. Second, if you change it
to use the full path name because of the duplicates problem then any
slashes in the name will create sub-nodes which probably isn't what
you want.  The AddSubtreeRaw() will take care of the slash problem,
but not the UTF8 problem.

bug 28298 is a placeholder for finishing up the work on nsIRegistry.
the current AddSubtree() -> AddKeyUTF8(), and we need to add a Unicode
AddKey(). (extend to all the "subtree" APIs).


>   registry->SetStringUTF8(pluginKey, kPluginsFilenameKey, tag->mFileName);
>   if (tag->mFullPath)
>     registry->SetStringUTF8(pluginKey, kPluginsFullpathKey, tag->mFullPath);
> 
>   // we use SetBytes instead of SetString to address special character issue, see Mozilla bug 108246
>   if(tag->mName)
>     registry->SetBytesUTF8(pluginKey, kPluginsNameKey, nsCRT::strlen(tag->mName) + 1, (PRUint8 *)tag->mName);

Not your code, but can't give sr= with this glaring problem. The comment
shows someone knew about the UTF8 problem at one point (there's even a bug
reference), but there are two broken places right above that. Those
SetStringUTF8
should be SetBytesUTF8, or convert the name and path to Unicode and use the
Unicode SetString (which will convert to UTF8 internally, so double conversion
cost).


>   // Add in each mimetype this plugin supports
>   for (int i=0; i<tag->mVariants; i++) {
>     char mimetypeKeyName[16];
>     nsRegistryKey mimetypeKey;
>     PR_snprintf(mimetypeKeyName, sizeof (mimetypeKeyName), "mimetype-%d", i);

Why not key mimetypes by their mimetype similar to the above? might make
it easier to discard old unsupported ones.

>-  // We dont want any old plugins that dont exist anymore hanging around
>-  // So remove and re-add the root of the plugins info
>-  nsresult rv = registry->RemoveSubtree(nsIRegistry::Common, kPluginsRootKey);
>-  

You've solved the growth problem, but now we're back to the issue
that obviously concerned the original author here: how do you deal with
obsolete data in the cache?
Attachment #69581 - Flags: needs-work+
The problem this patch is solving is that people are ending up with Megabyte
sized  (and growing) profile registries, with only a few K actual data in them.

There are alternate solutions:
 - implement the registry packing feature, and run it often
   (registry is locked while this happens, so make sure you're the only consumer)
 - Use a flat file for this data instead, or RDF, or mork, or whatever.
Ok I need to rephrase my question. It does solve the problem but removing the
RemoveSubtree() is going to introduce another problem - every plugin ever
registered is going to stay in our registry, get read on startup and acted upon
when a data of that mimetype shows up. And hence the call being there in the
first place.

Attached patch Using Pack() (obsolete) — Splinter Review
Turns out that we already are exporting pack via nsIRegistry interface. Here is
a simple 4 liner. I haven't tested it. Dan, do you think this would work.
We're exporting it, but it's not actually implemented :-( (see bug 10210)

Implementing it shouldn't be too hard. The old 4.x code is still there ifdef'd
out, but it's probably easier to write it correctly from scratch. One problem we
had in 4.x was how slow it was.. if you set buffer size to the size of the file
in question (on both the old and new registries) that problem may be reduced.

Basically you just enumerate through the registry, and for each key enumerate
its entries, writing that into a new registry

The patch looks like it'd solve this growth problem without reintroducing the
"old plugin" problem (assuming you implement Pack), but I'd start to worry about
startup performance. When does this packing occur, and how often? My registry
grows all the time, so it appears we're invalidating and re-writing our cache
each run -- in that case is it really doing us any good?

Doug's approach of reusing predictable keys seems more efficient, assuming some
of the problems can be worked out. Obviously you'd have to enumerate the
existing data and compare to what you're going to write first to see if any
really should be deleted (applies to both plugins and mime-types within
plugins). Given the linked-list structure of plugin tags maybe that'd be a pain
to do (O(2) problems?).
The plugin subtree will be written to only when plugins information change - new
plugins are added/deleted (or) someone calls javascript:plugins.refresh().

I am not beginning to like this pack thing. (why introduce new code)

I had another idea: How about writing all plugin information as a blob and
reading it as a blob and parsing it out. No subtree worries. 
dan, can you post a patch which fixes up these call sites and does what you are
suggesting?
dveditz - never mind.

Regarding obsolete data - I guess I could write a routine that stats() the
filename and if it is not there, remove that subtree?  We would still have
*some* bloat in this file doing this.  Thoughts?

Assuming that is good enough, I need to fix up the unicode key handling.  What
else is missing?
Dougt, if you are going that route how about storing a attr/value like:

currentToggleStamp : 1

both in the top and in each plugin when you write entried to the subtree. The
next time you write plugins to registry, do something like:

- get("currentToggleStamp", &toggleStamp);
- toggleStamp = (toggleStamp ? 0 : 1);
- write plugins to registry with this toggleStamp and update top toggleStamp.
- Do another pass and remove any plugin subtree that has a mismatched togglestamp

This should be cheaper than stats. Esp if you plan on stating everything again
(not just the removed plugins).
Dan, if I convert the two callers of SetStringUTF8 to SetBytesUTF8, will that
break existing clients?
Which patch are we talking about? If old clients try to GetString on bytes data
they'll get errors, yes, but I'm sure the wholesale changing of keys will be a
bigger problem for them.

Just change the root key you use and then old and new clients will live in
entirely separate namespaces. The new client will have to do a one-time read of
all the plugins, but since this bug was about how the file grew all the time it
sounds like we scan for plugins a lot anyway.
passing to neeti.
Assignee: dougt → neeti
Target Milestone: --- → mozilla1.0
shouldn't this be also done for the mozilla0.9.9 branch?
Neeti, what is the status of this bug?  
Priority: -- → P3
[adt needinfo] what's the net effect of this? How risky is the fix? What is the
fix... what is the meaning of life? :)
Whiteboard: [adt needinfo]
One way to solve this is to not use the registry at for plugins, and use a 
flatfile structure and rewriting the caching code. I think this is risky.

The second option is to incorporate dveditz, and dp's suggestions above in the 
above patch. In order to make the plugins filename as the key, we would need to 
use AddSubtreeRaw(...) instead of AddSubtree(...). THen we will have to change
the enumeration to one that can handle "raw" unprocessed nodes Per dveditz, this 
would solve the will take care of the slash problem, but not the UTF8 problem.

dveditz,dougt: I need clarification on what exactly needs to be done to solve 
the UTF8 problem? How much work does this involve?

Then we also need to convert the two callers of SetStringUTF8 to SetBytesUTF8. 
To not break existing clients, we will have to change the root key.

Regarding obsolete date, need to incorporate dp's suggestion about 
currentToggleStamp.

regarding comment #16, #17, we now have the ability to put plugin files in both
mozilla.org/mozilla/plugins and the user's application/data/mozilla/plugins
directories.. the first is what is normal case, the second is for installing
multiple builds without having to move and copy plugins to a new build.

-dennis
Also note:

I just moved my plugins out of my current build directory to my application
directory on build 2002-3-13, and it causes my hard drive to thrash less than
having them in the build directory/plugins.. the only plugin needed in that
directory is the one that comes with mozilla, the downloader plugin.  Once I
took it out of the build directory, mozilla couldn't find my plugins, and I got
a dialog to come up telling me about the downloader plugin is needed when I was
on a page that had shockwave or wmp content.  So we know that works.. so
anyway.. just hopes this helps you understand and fix the problem that much more.

plusing this bug as per adt plugins triage.
Keywords: nsbeta1nsbeta1+
Whiteboard: [adt needinfo] → [adt2]
topembed+ per EDT triage.
Keywords: topembedtopembed+
*** Bug 131851 has been marked as a duplicate of this bug. ***
about:plugins is taking 20 seconds on OpenVMS, I think because of appreg bloat. 
See bug 130322 for more details. Feel free to mark 130322 as a duplicate or a 
"depends on" if you think appropriate.
sorry for the spam, but I think my report is worth it: I've been using nightly
builds every day for 1 year, and my registry.dat is 20.8MB large (!). I didn't
touch it, perhaps I can be useful by measuring some loading time bench ?
Attached patch patch1 (obsolete) — Splinter Review
Using a flat file for storing plugin info instead of using libreg -

This patch uses a flatfile for storing plugin information. This is currently 
being stored in registry.txt/appreg.txt.

Instead of reading and writing the plugin information to registry.txt, we read 
and write from a flatfile pluginreg.dat which has the following format.


[HEADER]
version: x.x.x

[PLUGINS]

filename
lastModTimeStamp,description,name
mimetype1,description1,extension1
mimetype2,description21,extension2


TODO's

-Need to test these on linux and mac
Comment on attachment 80814 [details] [diff] [review]
patch1

- how about using \, or %, for escaping the comma. 
- indentations seem messed up. are you using evil tabs? 
- goto's can be avoided. just a nitpick though.
Attachment #80814 - Flags: needs-work+
Attached patch revised patch (obsolete) — Splinter Review
Attached a revised patch for review.
Comment on attachment 81738 [details] [diff] [review]
revised patch

Looks pretty good. 

Question: What's going to happen to the existing plugin info people's appreg's?

Can we avoid the goto's?

Can you update to the trunk tip so I can try out on my Mac? I'm wondering what
happens with mFilename/mFullpath?
Attached patch updated with the latest trunk tip. Removed the goto's. With my
current patch the existing plugin info in people's appreg's does not change. We
will not be reading or writing the plugin info to the appreg file.
Is there a real need to cache the list of plugins? Is it so much faster to 
cache the filenames/dates/mimetypes than to determine this information at run-
time (the first time any plugin is required)? Is the trade-off (flexibility)
worth it? 

Even if all of the above are true, the list of plugins doesn't belong in the 
registry. The plugin record is specific to a particular installation (read: 
machine), while the registry is per-user. 

I suggest putting the record in the same directory as the plugins themselves.
  - The list of plugins would be specific for that set of plugins. Things 
    will be fine even when the user's home directory is not on the same 
    machine as the Moz installation (think AFS and/or NT roaming profiles).
  - There would be no need to have canonical paths pointing to the plugins.
    Things will be fine even if the Moz installation "moves" (or the mount
    point is different).
  - Shared installations of Moz will not require users to do an
    about:plugins to update their registries.
  - The file can be in "native" format. Since plugins are not binary 
    compatible accross platforms, there is no reason why the record 
    needs to be XP safe either.
  - requiring superuser privileges to update the list of available plugins 
    is a good thing. Since it takes admin privileges to install (shared)
    plugins, requiring privs to update the record is ok.
    (incidentally, Moz could optimize the frequency with which it scans 
    for new plugins by first considering if the record is writable.)


>The plugin record is specific to a particular installation (read: 
>machine), while the registry is per-user. 

I do believe that mozilla supports per-user plugins, FWIW.   I have the
crossover plugins installed in my ~/.mozilla/plugins
Since it's a single-user only plugin (issues with WINE), it makes no sense for
me to have it installed system-wide.   (Otherwise mozilla crashes for other users)
Comment on attachment 82536 [details] [diff] [review]
revised patch updated with the latest trunk tip

r=peterl with the note that there might be old plugin cruft left in the appreg
from older browsers.

Caching this information is important for performance because some platforms
need much overhead to use NP_GetMIMEDescription() for getting MIME types.
Attachment #82536 - Flags: review+
*Sigh*! So, we're writing a bunch of new code because we don't want to finish
writing some code somewhere else? We're opening yet-another-file on startup? I
think this approach is wrong, wrong, wrong, and would rather see someone finish
implementing the registry's Pack() function.

I don't think this should be EDT+, ADT+, topembed+, or anything+. It only
manifests itself when someone regularly re-installs version after version after
version on a daily basis. Not a common user scenario, and something that a
Mozilla developer can deal with by clobbering the file (which, happily, will
rebuild itself). Removing said keywords for re-evaluation.
Whiteboard: [adt2]
Attachment #82536 - Flags: needs-work+
Although it is not apparent in the bug, i removed most of the requirement for
libreg in another bug (48888).  I asked neeti to rip out the plugin/libreg
requirement in hopes to remove libreg completely at some point.
Keywords: topembedtopembed-
*** Bug 146072 has been marked as a duplicate of this bug. ***
Re comment 54

Unfortunately, Chris, this plugin information is written to the same cryptic
binary file that the profile data is (which should arguably be in a
human-readable format). If you delete the file to reset the plugin cruft you
also delete knowledge of your profiles.

Crucial data like profile locations should never have been in a non-editable
file, but that mistake was compounded when other buggy code started putting
their data in the same file.
*** Bug 150221 has been marked as a duplicate of this bug. ***
Keywords: mozilla1.1
*** Bug 150344 has been marked as a duplicate of this bug. ***
Keywords: mozilla1.0mozilla1.0.1
*** Bug 130322 has been marked as a duplicate of this bug. ***
*** Bug 157627 has been marked as a duplicate of this bug. ***
This problem has become worse, it now seems to happen on every Netscape launch.
See bug 157627 and http://bugscape.netscape.com/show_bug.cgi?id=17790

Could we use 4.x #ifdef'ed Pack() function to "clean" the registry? There is
also a patch in bug 128366 to not load the plugin module at startup.
Like you're saying, this seems to block bug 157627. Normal severity. Seems like
a high priority bug. 
Blocks: 157627
Severity: minor → normal
Whiteboard: [Impact Needed]
renominating for topembed and making ADT1, as this is a serious problem, that
may corrupt users
profile.

this bug maybe related to bug 128366, and bugscape bug
http://bugzilla.mozilla.org/show_bug.cgi?id=128366
Whiteboard: [Impact Needed] → [ADT1 RTM] [ETA Needed]
Target Milestone: mozilla1.0 → mozilla1.0.1
neeti is out.  I will take.  You just want this fixed on the TRUNK right?
Assignee: neeti → dougt
as requested by ADT, I am adding a note to reference other related bugs:
bugscape bug 17790, assigned to AV: registry.dat grows on every start-up

bugzilla bug 157627, assigned to AV: bugzilla version of bugscape bug 17790

bugzilla bug 128366, assigned to peterl: OJI causes plugin DLL to load at
startup, scan for plugins, and load Java  DLL's
Whiteboard: [ADT1 RTM] [ETA Needed] → [Impact Needed]
Target Milestone: mozilla1.0.1 → mozilla1.0
Doug: I would suspect that we need this fixed on the trunk and the branch
Whiteboard: [Impact Needed] → [ADT1 RTM] [ETA Needed]
Target Milestone: mozilla1.0 → mozilla1.0.1
Attached patch update of neeti's work (obsolete) — Splinter Review
Attachment #69581 - Attachment is obsolete: true
Attachment #69614 - Attachment is obsolete: true
Attachment #80814 - Attachment is obsolete: true
Attachment #81738 - Attachment is obsolete: true
Attachment #82536 - Attachment is obsolete: true
Attached patch xpcom part of the patch (obsolete) — Splinter Review
okay.

The plugin team is going to review this patch.  However, I do not recommend that
this patch land for the branch without much testing on the trunk.  This patch
basically rewrites how we store plugin information.
Doug, last two patches are identical, and there is no xpcom part
by xpcom part i mean changes in nsPluginHostImpl::LoadXPCOMPlugins()
Attached patch slightly modified doug's patch (obsolete) — Splinter Review
During review under debugger I've took a courage to modify dougt's patch a
little bit, to get rid of unnecessary Un/Escape calls I copied
nsManifestLineReader.h
into mozilla/modules/plugin/base/src/sPluginManifestLineReader.h and changed
',' delimiter to '|', and also I removed strdup/free from
nsPluginHostImpl::ReadPluginInfo. Please review.
doug/serge: we are trying to wrap up 1.0.1. assuming we are agreed that serge's
last patch is the best solution, can you work to get reviews, so that we could
get this landed asap. 
Jamie read my comment #70.  Restated, I do not think that this patch should land
on the branch until there is *alot* more testing.  Given your schedule and the
above patch as the only proposed solution, I think that you may have to live
with this in the next release.  

Serge, your changes look good.  If you are happy with the inital changes, then I
am happy with yours.  Lets see about getting dveditz or alec to super review for
landing on the trunk.
Like Doug I'm concerned that this get thorough testing, but unlike Doug I don't
think we can pass on this for MachV. On the 7.0pr1 newsgroups people are
starting to notice 68Mb registry files. As mentioned before ee can't very well
tell users to delete the file because it also contains a pointer to their
profile, and they can't simply recreate *that* because of the random salt.

Serge's version of the patch does not include the escaping that Doug's does. We
can't trust that plugin info like description won't include format-busting
characters, and on Mac and Windows even the path could include ",". Doug's
escape function needs to escape the escaping character itself (and possibly \n
and \r in case of malicious/stupid plugin, although in that case we could argue
the user should delete the plugin instead because who knows how else it's
messing with their system).

Serge's patch doesn't do anything with "unwanted" plugins. Is there some reason
we don't need to cache those now?

Since you're currently rejecting the file if the version string doesn't match
exactly why not just compare the entire version line? It'd be tons cheaper than
parsing the line, converting strings to ints, and comparing each of the three
pieces individually.

There are a lot of places parsing the file where you appear to detect a bad
format but you try to carry on. Why not junk the file and load the plugins from
scratch? One example: if the mimetypecount is greater than your max (how?) you
reset it to max-1 and then read that many lines. If it were a correct number
you're now in an unexpected part of the file so you'll read junk, and if it was
a  bogus value then the file can't be trusted. Other places you simply break out
of reading the file early, but don't appear to note that fact in any way.

Ah, I see you changed the file delimiter to '|', probably to avoid having to
escape things. But that doesn't work: '|' might still show up in a description
somewhere. Any character you pick will have to be escaped because some fool will
find a way to use it. (might even be a common second byte value in a multibyte
asian character set, who knows?).
If we don't cache unwanted plugins we will re-write the cache info every time
even if nothing has changed. One way to avoid this is to check for unwantedness
not only when we read the info but also when we just scan to detect changes. But
the mechanism is quite fragile, I think the less we touch the better.
bug 142247 is a fine sample of what can happen because moz wants to write to
appreg all the time.
dugt: Can we at least pursue reviews and get this landed on the trunk asap, just
in case?
Whiteboard: [ADT1 RTM] [ETA Needed] → [ADT1 RTM] [ETA 07/24] [needs reviews]
dveditz:
I used '|' as delimiter because on wi32 it is mimytype delimiter only stored in
plugin dll, on unix & mac the same delimiter is ';', I doubt the plugin vendors
use it in different way, 4.x & mozilla code wont be able to parse such plugins
info.I made that delim definable, and I think with an initial knowledge of the
input data we can bypass the escaping code and speed up file parsing a little bit. 

>Serge's patch doesn't do anything with "unwanted" plugins. Is there some reason
>we don't need to cache those now?
it does include tag->mFlags, which contains NS_PLUGIN_FLAG_UNWANTED bit, along
with some other bits we have to save.

>Since you're currently rejecting the file if the version string doesn't match
>exactly why not just compare the entire version line?
I haven't touch that code. Doug?

>if the mimetypecount is greater than your max (how?)
I use that max only for stack allocation of
+    char *mimetypes[PLUGIN_REGISTRY_MAX_MIMETYPES_PER_PLUGIN];
anyway I do not like that part of code it'll and I'm going to rewrite it.

>Other places you simply break out
>of reading the file early, but don't appear to note that fact in any way
that is true, as far as we break out before nsPluginTag* tag = new nsPluginTag()
call,we do not fill out our internal structure with bad data, and every thing is
going to be the same as in case new plugins were installed.

so which attachment should I be reviewing? I see 3 non-obsolete patches here.
Doug I've marked all prev patches as obsolete, to avoid confusions.
Alec, would you sr= on this patch, please
Attachment #92002 - Attachment is obsolete: true
Attachment #92008 - Attachment is obsolete: true
Attachment #92072 - Attachment is obsolete: true
Comment on attachment 92267 [details] [diff] [review]
 patch v2, addresses dveditz's concerns

sr=alecf but boy this is big for 1.0 - so there's NO chance of a simple fix
that will just prevent rewriting the registry every time?

anyway, will be good to see this land on the trunk anyway.
See bug 157627 for the "simple fix that will just prevent rewriting the registry
every time". There is patch there that is just a few lines and pretty safe but
the fix in this bug needs to be done sooner or later on the trunk anyway.
However, if the patch in bug 157627 really does do the trick for the meantime,
then perhaps this could wait till post 1.1beta as I think drivers approval is
needed on the trunk too these days.
Comment on attachment 92267 [details] [diff] [review]
 patch v2, addresses dveditz's concerns

>+  while (1)
>+  {
     ...
>+     if (!reader.NextLine())
>+       break;
>+   }

Instead of the icky while(1) which tells the reader nothing about the loop,
I'd prefer "do{...}while (reader.NextLine());", but I guess since this is
tested and reviewed code don't change it now. Fix the brace alignment though.

>+      //description\n  --\  on separate line, so there is no need to parse it
>+      //name\n         --/  and check for delimiters

Well, OK, but those weren't the only parts of the file that needed to
be escaped just in case there's some broken plugin out there. But I guess
we've got a version stamp in the file, so we can fix that later. It's
not a security hole because an evil plugin would do its work directly.
I'm mostly worried that someone will produce a plugin with descriptions
in an Asian multibyte charset, or install with problem chars in the path
(again, most likely with a multibyte charset OS).

File a bug on it ("plugin cache format needs escaping") and I'll let
it go. It's certainly a minor concern compared to the problem we're
trying to fix.

In reading the plugin cache, why not get the size and set
flen before opening it, so if those two actions fail you don't
have to do the extra closes. And then close the file immediately
after reading so everything doesn't have to jump to out:

Then you could have passed ownership of the buffer to the
manifest reader, and had it do the free in its destructor and
done away with all the goto outs. Much cleaner and less likely
to get broken during future maintenance. Again, since this is tested
code I'm ambivalent about asking for changes, so you could sway me
into accepting this as-is by filing a bug on the cleanup.

sr=dveditz if you file those two bugs. It'd be good to get
alec's sr= as well -- since testing time will be short the more
eyes on the code the better.
Comment on attachment 92267 [details] [diff] [review]
 patch v2, addresses dveditz's concerns

Looks like here we may write excessive info:

+  nsPluginTag *taglist[] = {mPlugins,mCachedPlugins};
+  for (int i=0; i<sizeof(taglist)/sizeof(nsPluginTag *); i++) {
+    for (nsPluginTag *tag = taglist[i]; tag; tag=tag->mNext) {

In this double loop we cache everything from |mPlugins| which contains 'wanted'
plugins and everything from |mCachedPlugins| which normally contains _only_
unwanted plugins. But it is possible that the latter list contains more info:
if some plugin had been deleted before we started the application, its info
will remain in |mCachedPlugins| list as it is not going to be removed from
there when we scan folders and find plugins, because this deleted plugin cannot
obviously be found. In current code this is addressed by checking the flag, so
something like the following should suffice:

+  nsPluginTag *taglist[] = {mPlugins,mCachedPlugins};
+  for (int i=0; i<sizeof(taglist)/sizeof(nsPluginTag *); i++) {
+    for (nsPluginTag *tag = taglist[i]; tag; tag=tag->mNext) {
+      // store each plugin info into the registry
+      //filename|fullpath|lastModifiedTimeStamp|canUnload|tag->mFlags
+      if ((taglist[i] == mCachedPluigns) && 
+	   !(tag->mFlags & NS_PLUGIN_FLAG_UNWANTED))
+	   continue;
     [...]

This will ensure that we count only for 'unwanted' plugins when caching the
list and don't cache the ghost plugin info.
Comment on attachment 92267 [details] [diff] [review]
 patch v2, addresses dveditz's concerns

One more nit. We will crash with empty plugin list. Please do something like:

+nsPluginHostImpl::WritePluginInfo()
 {
+ NS_ENSURE_ARG_POINTER(mPlugins);
+ NS_ENSURE_ARG_POINTER(mCachedPlugins);

I am marking this 'needs-work', please yell on me loudly if disagree.
Attachment #92267 - Flags: needs-work+
Doug, once we decided on the approach to fix this, would not it make sense to
change the summary to something like 'Move plugin cache info from the
application registry to a flat file'? The problem which is described by current
summary is covered in bug 157627.
Changing to adt2 per the adt.
Whiteboard: [ADT1 RTM] [ETA 07/24] [needs reviews] → [ADT2] [needs reviews]
Comment on attachment 92267 [details] [diff] [review]
 patch v2, addresses dveditz's concerns

>+  while (1)
>+  {
>+    if (*reader.LinePtr() == '[')
>+    {
>+       char* p = reader.LinePtr() + (reader.LineLength() - 1);

Indentation got off too far by one right here, three spaces instead of two over
the previous line.

>+       if (*p != ']')
>+         break;

Underindented, but correct if you fix the if and all the other lines in the
outer if's then-block.

>+       *p = 0;
>+
>+        char* values[1];
>+        if (1 != reader.ParseLine(values, 1))
>+          break;
>+        // ignore the leading '['
>+        if (0 != PL_strcmp(values[0]+1, token)){
>+          if (!reader.NextLine())
>+            break;
>+          else
>+            continue;
>+        }
>+        return PR_TRUE;
>+     }
>+     
>+     if (!reader.NextLine())
>+       break;
>+   }

dveditz, I see absolutely no risk (no known C or C++ compiler bug where
do{...}while(c) was not equivalent to while(1){...if(!(c))break;}.  This loop
should be a do-while loop, not a while (1) infinite loop (aside: canonical
infinite loops in C use for (;;){...}).

/be
>+   return PR_FALSE;
>+}
>+
>+////////////////////////////////////////////////////////////////////////
> // Little helper struct to asynchronously reframe any presentations (embedded)
> // or reload any documents (full-page), that contained plugins 
> // which were shutdown as a result of a plugins.refresh(1)
>@@ -1162,11 +1198,12 @@
>        (mVariants != aPluginTag->mVariants) )
>     return PR_FALSE;
> 
>-  if (0 != mVariants && mMimeTypeArray && aPluginTag->mMimeTypeArray)
>-    for (PRInt32 i = 0; i < mVariants; i++)
>+  if (0 != mVariants && mMimeTypeArray && aPluginTag->mMimeTypeArray) {
>+    for (PRInt32 i = 0; i < mVariants; i++) {
>       if (PL_strcmp(mMimeTypeArray[i], aPluginTag->mMimeTypeArray[i]) != 0)
>         return PR_FALSE;
>-
>+    }
>+  }
>   return PR_TRUE;
> }
> 
>@@ -5095,20 +5132,9 @@
> 
>   *aPluginsChanged = PR_FALSE;
>   nsresult rv;
>-  
>-  // If the create instance failed, then it automatically disables all
>-  // our caching functions and we will just end up loading the plugins
>-  // on startup.
>-  nsCOMPtr<nsIRegistry> registry = do_CreateInstance(kRegistryCID);
>-  if (registry) {
>-    rv = registry->OpenWellKnownRegistry(nsIRegistry::ApplicationRegistry);
>-    if (NS_FAILED(rv)) {
>-      registry = nsnull;
>-    }
>-  }
> 
>-  // Load cached plugins info
>-  LoadCachedPluginsInfo(registry);
>+  // Read cached plugins info
>+  ReadPluginInfo();
> 
>   nsCOMPtr<nsIComponentManager> compManager = do_GetService(kComponentManagerCID, &rv);
>   if (compManager) 
>@@ -5241,7 +5267,7 @@
>   // if we are creating the list, it is already done;
>   // update the plugins info cache if changes are detected
>   if (*aPluginsChanged)
>-    CachePluginsInfo(registry);
>+    WritePluginInfo();
> 
>   // No more need for cached plugins. Clear it up.
>   ClearCachedPluginInfoList();
>@@ -5457,55 +5483,6 @@
>   return rv;
> }
> 
>-
>-static nsresult
>-AddPluginInfoToRegistry(nsIRegistry* registry, nsRegistryKey top,
>-                        nsPluginTag *tag, const char *keyname)
>-{
>-  NS_ENSURE_ARG_POINTER(tag);
>-  nsRegistryKey pluginKey;
>-  nsresult rv = registry->AddSubtree(top, keyname, &pluginKey);
>-  if (NS_FAILED(rv)) return rv;
>-
>-  // Add filename, name and description
>-  registry->SetStringUTF8(pluginKey, kPluginsFilenameKey, tag->mFileName);
>-  if (tag->mFullPath)
>-    registry->SetStringUTF8(pluginKey, kPluginsFullpathKey, tag->mFullPath);
>-
>-  // we use SetBytes instead of SetString to address special character issue, see Mozilla bug 108246
>-  if(tag->mName)
>-    registry->SetBytesUTF8(pluginKey, kPluginsNameKey, strlen(tag->mName) + 1, (PRUint8 *)tag->mName);
>-  
>-  if(tag->mDescription)
>-    registry->SetBytesUTF8(pluginKey, kPluginsDescKey, strlen(tag->mDescription) + 1, (PRUint8 *)tag->mDescription);
>-
>-  registry->SetLongLong(pluginKey, kPluginsModTimeKey, &(tag->mLastModifiedTime));
>-  registry->SetInt(pluginKey, kPluginsCanUnload, tag->mCanUnloadLibrary);
>-
>-  // Add in each mimetype this plugin supports
>-  for (int i=0; i<tag->mVariants; i++) {
>-    char mimetypeKeyName[16];
>-    nsRegistryKey mimetypeKey;
>-    PR_snprintf(mimetypeKeyName, sizeof (mimetypeKeyName), "mimetype-%d", i);
>-    rv = registry->AddSubtree(pluginKey, mimetypeKeyName, &mimetypeKey);
>-    if (NS_FAILED(rv))
>-      break;
>-    registry->SetStringUTF8(mimetypeKey, kPluginsMimeTypeKey, tag->mMimeTypeArray[i]);
>-
>-    if(tag->mMimeDescriptionArray && tag->mMimeDescriptionArray[i])
>-      registry->SetBytesUTF8(mimetypeKey, kPluginsMimeDescKey, 
>-                             strlen(tag->mMimeDescriptionArray[i]) + 1, 
>-                             (PRUint8 *)tag->mMimeDescriptionArray[i]);
>-
>-    registry->SetStringUTF8(mimetypeKey, kPluginsMimeExtKey, tag->mExtensionsArray[i]);
>-  }
>-  if (NS_FAILED(rv))
>-    rv = registry->RemoveSubtree(top, keyname);
>-
>-  return rv;
>-}
>-
>-
> ////////////////////////////////////////////////////////////////////////
> nsresult
> nsPluginHostImpl::LoadXPCOMPlugins(nsIComponentManager* aComponentManager)
>@@ -5594,124 +5571,293 @@
>   return NS_OK;
> }
> 
>-
> nsresult
>-nsPluginHostImpl::LoadCachedPluginsInfo(nsIRegistry* registry)
>+nsPluginHostImpl::WritePluginInfo()
> {
>-  // Loads cached plugin info from registry
>-  //
>-  // Enumerate through that list now, creating an nsPluginTag for
>-  // each.
>-  if (! registry)
>-    return NS_ERROR_FAILURE;
> 
>-  nsRegistryKey pluginsKey;
>-  nsresult rv = registry->GetSubtree(nsIRegistry::Common, kPluginsRootKey, &pluginsKey);
>-  if (NS_FAILED(rv)) return rv;
>+  nsresult rv = NS_OK;
>+  nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
>+  if (NS_FAILED(rv))
>+    return rv;
> 
>-  // Make sure we are dealing with the same version number of the plugin info
>-  nsXPIDLCString version;
>-  rv = registry->GetStringUTF8(pluginsKey, kPluginsVersionKey, getter_Copies(version));
>-  if (NS_FAILED(rv) || PL_strcmp(version.get(), kPluginInfoVersion)) {
>-    // Version mismatch
>-    // Nuke the subtree
>-    registry->RemoveSubtree(nsIRegistry::Common, kPluginsRootKey);
>-    PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
>-           ("LoadCachedPluginsInfo : Version %s mismatch - Expected %s. Nuking cached info.\n",
>-            version.get(), kPluginInfoVersion));
>-    return NS_ERROR_FAILURE;
>-  }
>+  directoryService->Get(NS_APP_APPLICATION_REGISTRY_DIR, NS_GET_IID(nsIFile), 
>+                        getter_AddRefs(mPluginsDir));
> 
>-  // XXX get rid nsIEnumerator someday!
>-  nsCOMPtr<nsIEnumerator> enumerator;
>-  rv = registry->EnumerateSubtrees(pluginsKey, getter_AddRefs(enumerator));
>-  if (NS_FAILED(rv)) return rv;
>+  if (!mPluginsDir)
>+    return NS_ERROR_FAILURE;
> 
>-  nsCOMPtr<nsISimpleEnumerator> plugins;
>-  rv = NS_NewAdapterEnumerator(getter_AddRefs(plugins), enumerator);
>-  if (NS_FAILED(rv)) return rv;
>+  PRFileDesc* fd = nsnull;
>+ 
>+  nsCOMPtr<nsIFile> pluginReg;
> 
>-  for (;;) {
>-    PRBool hasMore;
>-    plugins->HasMoreElements(&hasMore);
>-    if (! hasMore)
>-      break;
>+  rv = mPluginsDir->Clone(getter_AddRefs(pluginReg));       
>+  if (NS_FAILED(rv))
>+    return rv;
>+ 
>+  rv = pluginReg->AppendNative(kPluginRegistryFilename);
>+  if (NS_FAILED(rv))
>+    return rv;
> 
>-    nsCOMPtr<nsISupports> isupports;
>-    plugins->GetNext(getter_AddRefs(isupports));
>+  nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(pluginReg, &rv);
>+  if (NS_FAILED(rv))
>+    return rv;
> 
>-    nsCOMPtr<nsIRegistryNode> node = do_QueryInterface(isupports);
>-    NS_ASSERTION(node != nsnull, "not an nsIRegistryNode");
>-    if (! node)
>-      continue;
>+  rv = localFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
>+  if (NS_FAILED(rv))
>+    return rv;
> 
>-    // Pull out the information for an individual plugin, and link it
>-    // in to the mCachedPlugins list.
>-    nsRegistryKey key;
>-    node->GetKey(&key);
>+  PR_fprintf(fd, "Generated File. Do not edit.\n");
> 
>-    nsPluginTag* tag = nsnull;
>-    rv = LoadXPCOMPlugin(registry, nsnull, key, &tag);
>-    if (NS_FAILED(rv))
>-      continue;
>+  PR_fprintf(fd, "\n[HEADER]\nVersion%c%d%c%d\n",
>+             PLUGIN_REGISTRY_FIELD_DELIMITER,
>+             PLUGIN_REGISTRY_VERSION_MAJOR,
>+             PLUGIN_REGISTRY_FIELD_DELIMITER,
>+             PLUGIN_REGISTRY_VERSION_MINOR);
> 
>-    tag->SetHost(this);
>+  // Store all plugins in the mPlugins list - all plugins currently in use.
>+  PR_fprintf(fd, "\n[PLUGINS]");
> 
>-    // Mark plugin as loaded from cache
>-    tag->Mark(NS_PLUGIN_FLAG_FROMCACHE);
>-    PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
>-           ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName));
>-    tag->mNext = mCachedPlugins;
>-    mCachedPlugins = tag;
>+  nsPluginTag *taglist[] = {mPlugins,mCachedPlugins};
>+  for (int i=0; i<sizeof(taglist)/sizeof(nsPluginTag *); i++) {
>+    for (nsPluginTag *tag = taglist[i]; tag; tag=tag->mNext) {
>+      // store each plugin info into the registry
>+      //filename|fullpath|lastModifiedTimeStamp|canUnload|tag->mFlags
>+      PR_fprintf(fd, "\n%s%c%s%c%lld%c%d%c%lu\n",
>+        (tag->mFileName ? tag->mFileName : ""),
>+        PLUGIN_REGISTRY_FIELD_DELIMITER,
>+        (tag->mFullPath ? tag->mFullPath : ""),
>+        PLUGIN_REGISTRY_FIELD_DELIMITER,
>+        tag->mLastModifiedTime,PLUGIN_REGISTRY_FIELD_DELIMITER,
>+        tag->mCanUnloadLibrary,PLUGIN_REGISTRY_FIELD_DELIMITER,
>+        tag->mFlags);
>+      
>+      //description\n  --\  on separate line, so there is no need to parse it
>+      //name\n         --/  and check for delimiters
>+      //mtypecount
>+      PR_fprintf(fd, "%s\n%s\n%d\n", 
>+        tag->mDescription,
>+        tag->mName,
>+        tag->mVariants);
>+      
>+      // Add in each mimetype this plugin supports
>+      for (int i=0; i<tag->mVariants; i++) {
>+        PR_fprintf(fd, "%d%c%s%c%s%c%s\n",
>+          i,PLUGIN_REGISTRY_FIELD_DELIMITER,
>+          tag->mMimeTypeArray[i],PLUGIN_REGISTRY_FIELD_DELIMITER,
>+          tag->mMimeDescriptionArray[i],PLUGIN_REGISTRY_FIELD_DELIMITER,
>+          tag->mExtensionsArray[i]);
>+      }
>+    }
>   }
> 
>+  if (fd)
>+    PR_Close(fd);
>   return NS_OK;
> }
> 
> nsresult
>-nsPluginHostImpl::CachePluginsInfo(nsIRegistry* registry)
>+nsPluginHostImpl::ReadPluginInfo()
> {
>-  // Save plugin info from registry
>+  nsManifestLineReader reader;
>+  nsresult rv;
> 
>-  if (! registry)
>-    return NS_ERROR_FAILURE;
>+  nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
>+  if (NS_FAILED(rv))
>+    return rv;
> 
>-  // We don't want any old plugins that don't exist anymore hanging around
>-  // So remove and re-add the root of the plugins info
>-  nsresult rv = registry->RemoveSubtree(nsIRegistry::Common, kPluginsRootKey);
>-  
>-  nsRegistryKey pluginsSubtreeKey;
>-  rv = registry->AddSubtree(nsIRegistry::Common, kPluginsRootKey, &pluginsSubtreeKey);
>-  if (NS_FAILED(rv)) return rv;
>+  directoryService->Get(NS_APP_APPLICATION_REGISTRY_DIR, NS_GET_IID(nsIFile),
>+                        getter_AddRefs(mPluginsDir));
>+   
>+  if (!mPluginsDir)
>+    return NS_ERROR_FAILURE;  
> 
>-  rv = registry->SetStringUTF8(pluginsSubtreeKey, kPluginsVersionKey, kPluginInfoVersion);
>-  if (NS_FAILED(rv)) return rv;
>+  PRFileDesc* fd = nsnull;
>+ 
>+  nsCOMPtr<nsIFile> pluginReg;
>+ 
>+  rv = mPluginsDir->Clone(getter_AddRefs(pluginReg));
>+  if (NS_FAILED(rv))
>+    return rv;
> 
>-  int count = 0;
>+  rv = pluginReg->AppendNative(kPluginRegistryFilename);
>+  if (NS_FAILED(rv))
>+    return rv;
> 
>-  // Store all plugins in the mPlugins list - all plugins currently in use.
>-  char pluginKeyName[64];
>-  nsPluginTag *tag;
>-  for(tag = mPlugins; tag; tag=tag->mNext) {
>-    // store each plugin info into the registry
>-    PR_snprintf(pluginKeyName, sizeof(pluginKeyName), "plugin-%d", ++count);
>-    AddPluginInfoToRegistry(registry, pluginsSubtreeKey, tag, pluginKeyName);
>+  nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(pluginReg, &rv);
>+  if (NS_FAILED(rv))
>+    return rv;
>+
>+  rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
>+  if (NS_FAILED(rv))
>+    return rv;
>+
>+  PRInt64 fileSize;
>+  rv = localFile->GetFileSize(&fileSize);
>+  if (NS_FAILED(rv)) {
>+    PR_Close(fd);
>+    return rv;
>+  }
>+
>+  PRInt32 flen = nsInt64(fileSize);
>+  if (flen == 0) {
>+    PR_Close(fd);
>+    NS_WARNING("Plugins Registry Empty!");
>+    return NS_OK; // ERROR CONDITION
>   }
>+ 
>+  // set rv to return an error on goto out 
>+  rv = NS_ERROR_FAILURE;
> 
>-  // Store any unwanted plugins info so that we wont have to load
>-  // them again the next time around
>-  tag = mCachedPlugins;
>-  for(tag = mCachedPlugins; tag; tag=tag->mNext) {
>-    // Look for unwanted plugins
>-    if (!(tag->mFlags & NS_PLUGIN_FLAG_UNWANTED))
>+  char* registry = new char[flen+1];
>+  if (!registry) {
>+    goto out;
>+  }
>+    
>+  if (flen > PR_Read(fd, registry, flen)) {
>+    goto out;
>+  }
>+
>+  registry[flen] = '\0';
>+    
>+  reader.Init(registry, flen);
>+   
>+  if (!ReadSectionHeader(reader, "HEADER")) {
>+    goto out;
>+  }
>+
>+  if (!reader.NextLine()) {
>+    goto out;
>+  }
>+
>+  char* values[6]; 
>+    
>+  // VersionLiteral,major,minor
>+  if (3 != reader.ParseLine(values, 3)) {
>+    goto out;
>+  }
>+    
>+  // VersionLiteral
>+  if (PL_strcmp(values[0], "Version")) {
>+    goto out;
>+  }
>+   
>+  // major
>+  if (PLUGIN_REGISTRY_VERSION_MAJOR != atoi(values[1])) {
>+    goto out;
>+  }
>+   
>+  // minor
>+  if (PLUGIN_REGISTRY_VERSION_MINOR != atoi(values[2])) {
>+    goto out;
>+  }
>+
>+  if (!ReadSectionHeader(reader, "PLUGINS")) {
>+    goto out;
>+  }
>+
>+  while (1) {
>+
>+    if(!reader.NextLine())
>+      goto out;
>+       
>+    //filename|fullpath|lastModifiedTimeStamp|canUnload|tag.mFlag
>+    if (5 != reader.ParseLine(values, 5))
>+      break;
>+
>+    char *filename = values[0];
>+    char *fullpath = values[1];
>+    PRInt64 lastmod = nsCRT::atoll(values[2]);
>+    PRBool canunload = atoi(values[3]);
>+    PRUint32 tagflag = atoi(values[4]);
>+
>+    //description is whole line and can contain any chars
>+    if (!reader.NextLine())
>+      goto out;
>+    char *description = reader.LinePtr();
>+
>+    //name is whole line and can contain any chars
>+    if (!reader.NextLine())
>+      goto out;
>+    char *name = reader.LinePtr();
>+
>+    //mimetypecount
>+    if (!reader.NextLine())
>+      goto out;    
>+    int mimetypecount = atoi(reader.LinePtr());
>+
>+    char *stackalloced[PLUGIN_REGISTRY_MAX_MIMETYPES_PER_PLUGIN * 3];
>+    char **mimetypes;
>+    char **mimedescriptions;
>+    char **extensions;
>+    char **heapalloced = 0;
>+    if (mimetypecount > PLUGIN_REGISTRY_MAX_MIMETYPES_PER_PLUGIN - 1) {
>+      heapalloced = new char *[mimetypecount * 3];
>+      mimetypes = heapalloced;
>+    } else {
>+      mimetypes = stackalloced;
>+    }
>+    mimedescriptions = mimetypes + mimetypecount;
>+    extensions = mimedescriptions + mimetypecount;
>+
>+    int mtr = 0; //mimetype read
>+    for (; mtr < mimetypecount; mtr++) {
>+      if (!reader.NextLine())
>+        break;
>+           
>+      //line number|mimetype|description|extension
>+      if (4 != reader.ParseLine(values, 4))
>+        break;
>+      int line = atoi(values[0]);
>+      if (line != mtr)
>+        break; 
>+      mimetypes[mtr] = values[1];
>+      mimedescriptions[mtr] = values[2];
>+      extensions[mtr] = values[3];
>+    }
>+
>+    if (mtr != mimetypecount) {
>+      if (heapalloced) {
>+        delete [] heapalloced;
>+      }
>+      goto out;
>+    }
>+    
>+    nsPluginTag* tag = new nsPluginTag(name,
>+                                       description,
>+                                       filename,
>+                                       (*fullpath ? fullpath : 0), // we have to pass 0 prt if it's empty str
>+                                       (const char* const*)mimetypes,
>+                                       (const char* const*)mimedescriptions,
>+                                       (const char* const*)extensions,
>+                                       mimetypecount, lastmod, canunload);
>+    if (heapalloced) {
>+        delete [] heapalloced;
>+    }
>+
>+    if (!tag) {
>       continue;
>+    }
>+
>+    // Mark plugin as loaded from cache
>+    tag->Mark(NS_PLUGIN_FLAG_FROMCACHE);
>+    PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
>+           ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName));
>+    tag->mFlags = tagflag;
>+    tag->mNext = mCachedPlugins;
>+    mCachedPlugins = tag;
> 
>-    // store each plugin info into the registry
>-    PR_snprintf(pluginKeyName, sizeof(pluginKeyName), "plugin-%d", ++count);
>-    AddPluginInfoToRegistry(registry, pluginsSubtreeKey, tag, pluginKeyName);
>   }
> 
>-  return NS_OK;
>+  rv = NS_OK;
>+
>+out:
>+  if (fd)
>+    PR_Close(fd);
>+    
>+  if (registry)
>+    delete [] registry;
>+
>+  return rv;
> }
> 
> nsPluginTag *
>Index: nsPluginHostImpl.h
>===================================================================
>RCS file: /cvsroot/mozilla/modules/plugin/base/src/nsPluginHostImpl.h,v
>retrieving revision 1.83
>diff -u -r1.83 nsPluginHostImpl.h
>--- nsPluginHostImpl.h	14 Jul 2002 00:39:35 -0000	1.83
>+++ nsPluginHostImpl.h	22 Jul 2002 20:07:20 -0000
>@@ -434,11 +434,11 @@
> 
>   PRBool IsRunningPlugin(nsPluginTag * plugin);
> 
>-  // Loads all cached plugins info into mCachedPlugins
>-  nsresult LoadCachedPluginsInfo(nsIRegistry* registry);
>-
>   // Stores all plugins info into the registry
>-  nsresult CachePluginsInfo(nsIRegistry* registry);
>+  nsresult WritePluginInfo();
>+
>+  // Loads all cached plugins info into mCachedPlugins
>+  nsresult ReadPluginInfo();
> 
>   // Given a filename, returns the plugins info from our cache
>   // and removes it from the cache.
>@@ -473,6 +473,7 @@
>   nsActivePluginList mActivePluginList;
>   nsVoidArray mUnusedLibraries;
> 
>+  nsCOMPtr<nsIFile> mPluginsDir;
>   nsCOMPtr<nsIDirectoryServiceProvider> mPrivateDirServiceProvider;  
>   nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
> 
>Index: nsPluginManifestLineReader.h
>===================================================================
>RCS file: nsPluginManifestLineReader.h
>diff -N nsPluginManifestLineReader.h
>--- /dev/null	1 Jan 1970 00:00:00 -0000
>+++ nsPluginManifestLineReader.h	22 Jul 2002 20:07:20 -0000
>@@ -0,0 +1,126 @@
>+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
>+/* ***** BEGIN LICENSE BLOCK *****
>+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
>+ *
>+ * 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 the Initial Developer are Copyright (C) 1998
>+ * the Initial Developer. All Rights Reserved.
>+ *
>+ * Contributor(s):
>+ *
>+ * Alternatively, the contents of this file may be used under the terms of
>+ * either the GNU General Public License Version 2 or later (the "GPL"), or 
>+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
>+ * in which case the provisions of the GPL or the LGPL are applicable instead
>+ * of those above. If you wish to allow use of your version of this file only
>+ * under the terms of either the GPL or the LGPL, and not to allow others to
>+ * use your version of this file under the terms of the NPL, indicate your
>+ * decision by deleting the provisions above and replace them with the notice
>+ * and other provisions required by the GPL or the LGPL. If you do not delete
>+ * the provisions above, a recipient may use your version of this file under
>+ * the terms of any one of the NPL, the GPL or the LGPL.
>+ *
>+ * ***** END LICENSE BLOCK ***** */
>+
>+#ifndef nsPluginManifestLineReader_h__
>+#define nsPluginManifestLineReader_h__
>+
>+#include "nspr.h"
>+#include "nsDebug.h"
>+
>+
>+#define PLUGIN_REGISTRY_VERSION_MINOR 6
>+#define PLUGIN_REGISTRY_VERSION_MAJOR 0
>+#ifdef XP_WIN
>+#define PLUGIN_REGISTRY_FIELD_DELIMITER '|'
>+#else
>+#define PLUGIN_REGISTRY_FIELD_DELIMITER ':'
>+#endif
>+
>+#define PLUGIN_REGISTRY_MAX_MIMETYPES_PER_PLUGIN 64
>+
>+class nsManifestLineReader
>+{
>+public:
>+    nsManifestLineReader() : mBase(nsnull) {} 
>+    ~nsManifestLineReader() {}
>+
>+    void Init(char* base, PRUint32 flen) 
>+    {
>+        mBase = mCur = mNext = base; 
>+        mLength = 0;
>+        mLimit = base + flen;
>+    }
>+
>+    PRBool NextLine()
>+    {
>+        if(mNext >= mLimit)
>+            return PR_FALSE;
>+        
>+        mCur = mNext;
>+        mLength = 0;
>+        
>+        while(mNext < mLimit)
>+        {
>+            if(IsEOL(*mNext))
>+            {
>+                *mNext = '\0';
>+                for(++mNext; mNext < mLimit; ++mNext)
>+                    if(!IsEOL(*mNext))
>+                        break;
>+                return PR_TRUE;
>+            }
>+            ++mNext;
>+            ++mLength;
>+        }
>+        return PR_FALSE;        
>+    }
>+
>+    int ParseLine(char** chunks, int maxChunks)
>+    {
>+        NS_ASSERTION(mCur && maxChunks && chunks, "bad call to ParseLine");
>+        int found = 0;
>+        chunks[found++] = mCur;
>+        
>+        if(found < maxChunks)
>+        {
>+            for(char* cur = mCur; *cur; cur++)
>+            {
>+                if(*cur == PLUGIN_REGISTRY_FIELD_DELIMITER)
>+                {
>+                    *cur = 0;
>+                    chunks[found++] = cur+1;
>+                    if(found == maxChunks)
>+                        break;
>+                }
>+            }
>+        }
>+        return found;
>+    }
>+
>+    char*       LinePtr() {return mCur;}    
>+    PRUint32    LineLength() {return mLength;}    
>+
>+    PRBool      IsEOL(char c) {return c == '\n' || c == '\r';}
>+private:
>+    char*       mCur;
>+    PRUint32    mLength;
>+    char*       mNext;
>+    char*       mBase;
>+    char*       mLimit;
>+};
>+
>+#endif
Attached patch patch v3 (obsolete) — Splinter Review
it addresses dveditz's concerns, except  "plugin cache format needs escaping",
as I said above '|' is mimetype descriptions delimiter on win32 as well as ':'
is unix/mac delimiter and current plugin implementation does not support
multibyte charset for mimetypes.
Thanks av for NS_PLUGIN_FLAG_UNWANTED addon.
BTW:
+ for (nsPluginTag *tag = taglist[i]; tag; tag=tag->mNext) {
does the right thing if mPlugins or mCachedPlugins == 0, so there's no need to
check it somewhere else
Attachment #92267 - Attachment is obsolete: true
Attachment #92311 - Attachment is obsolete: true
Comment on attachment 92320 [details] [diff] [review]
patch v4 addresses brendan comments

D'oh!  I thought I was clearly favoring a do-while loop (do {...} while
(reader.NextLine()); to be precise), instead of  either while (1){...; if
(!reader.NextLine()) break;} or for (;;){...; if (!reader.NextLine()) break;})
as dveditz suggested.  There is no reason to obfuscate the control flow here.

/be
Attachment #92320 - Attachment is obsolete: true
Comment on attachment 92326 [details] [diff] [review]
patch v5 with do {} while() in ReadSectionHeader()

>+  do {
>+    if (*reader.LinePtr() == '[') {
>+      char* p = reader.LinePtr() + (reader.LineLength() - 1);
>+      if (*p != ']')
>+        break;
>+      *p = 0;
>+      
>+      char* values[1];
>+      if (1 != reader.ParseLine(values, 1))
>+        break;
>+      // ignore the leading '['
>+      if (0 != PL_strcmp(values[0]+1, token)) {
>+        if (!reader.NextLine())
>+          break;
>+        else
>+          continue;
>+      }
>+      return PR_TRUE;
>+    }
>+  } while (reader.NextLine());

First, else after break is unnecessary and was in the old patch -- but second,
and more important, now that you've moved the reader.NextLine() into the loop
control, the whole if-else:

+	 if (!reader.NextLine())
+	   break;
+	 else
+	   continue;

is wrong.  It will skip an extra line until end of file.  All you need is:

+      if (0 != PL_strcmp(values[0]+1, token))
+	 continue;

As a matter of style, I see no point in the 0 != here, but I'd better shut up. 
One more patch, please, and read the whole loop yourself to be convinced that
it's right (I wasn't posing a trick question when I seconded dveditz's call for
the do-while loop, honest!  I would hate to let a bug get introduced by such a
change, though).

/be
Attachment #92326 - Flags: needs-work+
Attachment #92326 - Attachment is obsolete: true
Comment on attachment 92332 [details] [diff] [review]
patch v6 with correction in ReadSectionHeader()

Please try mentally executing the code to see that it takes the intended steps,
and/or the same steps as the previous patches -- you need a continue, not a
break, if PL_strcmp (which isn't needed here, strcmp is supported on all
platforms, but there I go again :-) returns non-zero.  It'd be good to see a
tested patch at this point....

/be
Attachment #92332 - Flags: needs-work+
serge: I followed those lxr links, thanks.  Something's clearly amiss, starting
with attachment 80814 [details] [diff] [review] in this bug ("patch1") at least.  That patch and all later
ones till your last, attachment 92332 [details] [diff] [review], *ocntinued* after PL_strcmp returned a
nono-zero result (unless at end of file, i.e., unless NextLine returned false).
 Hence my last comment, and needs-work mark.

Now that I follow the links you gave, I see the code in xpcom breaking, not
continuing, on a mismatch with token.  That could be right for the xpcom cases,
but wrong here -- it depends on how ReadSectionHeader is meant to be called. 
Should it keep on searching for a matching [token...]?  Or should it give up if
the next [...] line does not begin (after the [) with the desired token?

/be
Argh, fingers tired:

s/*ocntinued*/*continued*/

/be
>it depends on how ReadSectionHeader is meant to be called
Brendan: you are right it depends, and in this particular
nsPluginHostImpl::ReadPluginInfo() implementation I see no pint to continue
searching for section header, if we fail to get exact header from file with
liner headers structure.
Comment on attachment 92332 [details] [diff] [review]
patch v6 with correction in ReadSectionHeader()

Ok, if you're sure of the section header order, then the change you made to
break rather than continue is ok with me.  I'll defer to dveditz for restamping
sr= here.

/be
Attachment #92332 - Flags: needs-work+
Comment on attachment 92332 [details] [diff] [review]
patch v6 with correction in ReadSectionHeader()

r=av
Attachment #92332 - Flags: review+
Comment on attachment 92332 [details] [diff] [review]
patch v6 with correction in ReadSectionHeader()

sr=dveditz
Attachment #92332 - Flags: superreview+
over to serge who will land this after 1.1b
Assignee: dougt → serge
on the trunk 
/mozilla/modules/plugin/base/src/nsPluginManifestLineReader.h,v
initial revision: 1.1
/mozilla/modules/plugin/base/src/nsPluginHostImpl.h,v 
new revision: 1.85; previous revision: 1.84
/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp,v
new revision: 1.412; previous revision: 1.411
Status: NEW → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
Was it ever determined whether or not this patch will clean up registries that
are already inflated by this problem, or is another bug needed for that? As
mentioned previously in this bug, it also contains profile data, so simply
deleting it isn't a good option.
Can can delete the file, run mozilla (and the profilemanager will comes up with
no profiles) and create the profile again with the EXACT same name.
Mozilla will use the old profile data (works on linux and win32)
no, this patch (attachment 92332 [details] [diff] [review]) does not implement registry Pack(),
and registry.dat remains untouched.
FWIW, it appears comment 26 refers to the registry packing implementation bug.
I don't know if this bug is resolved.
I'm running 1.1, and (see comment 107) appreg does not
get smaller (about 550k here), and deleting it
(as in comment 108) no longer works:
I get 'A profile with the name han already exists,
please use another name' os somthing very like it.
If I let mozilla use my existing registry, it says it
has to convert, and I lose my signon file and wallet.
I really cannot remember all those passwords mtself !
ham, I did deleting registry an recreating it again several times w/o any problem,
if you afraid to lose the data make a back up copy of it
*** Bug 179389 has been marked as a duplicate of this bug. ***
Updating QA contact to bmartin@netscape.com
QA Contact: shrir → bmartin
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: