Closed Bug 297312 Opened 19 years ago Closed 19 years ago

EM gets confused between multiple installations of the same version

Categories

(Toolkit :: Startup and Profile System, defect, P1)

defect

Tracking

()

RESOLVED FIXED
mozilla1.8beta4

People

(Reporter: benjamin, Assigned: benjamin)

References

Details

(Whiteboard: [no l10n impact] has patch, needs review darin)

Attachments

(1 file, 5 obsolete files)

The Extension Manager cache (extensions-startup.manifest) can get confused if you have two Firefoxen of the same app.extensions.version installed in different directories: it will frequently end up trying to load an extension from A/extensions/<foo> when you launch B/firefox. In addition, the EM will cease to work at all in some cases because it uses nsILocalFile.[set]persistentDescriptor without a try/catch block. [set]persistentDescriptor can and will fail on mac if the parent directory has been removed. I intend to solve this problem through some defense-in-depth: 1) store the last-run-from path and revalidate the startup cache if we changed paths 2) use a new form of relative descriptor in the extensions-startup cache, similar to the form used in compreg.dat: it will be <dirservicekey>:<relativedescriptor> or abs:<persistentdescriptor> 3) Add lots of additional sanity-checking to the startup cache, and freely invalidate it if things go wonky, and don't invalidate extensions.rdf if extensins-startup.manifest was removed.
Depends on: 297315
This is a persistent-descriptor service that can be used to implement #2 here. I ended up using percent-sign instead of colon because resource:app has a colon in it and that can confuse things.
Attachment #187969 - Flags: first-review?(shaver)
Need to solve this at least one way for 1.8b4.
Flags: blocking1.8b4+
Comment on attachment 187969 [details] [diff] [review] Part 1, rev. 1: make a nsIPersistentDescriptorService >+ /** >+ * Get a string descriptor that represents an nsILocalFile relative >+ * to a list of directoryservice keys specified by aCategory. If the >+ * file is not relative to any specified directoryservice key, an absolute >+ * descriptor will be used. >+ */ >+ ACString getDescriptor(in nsILocalFile aFile, in string aCategory); Where are the categories likely to be documented, or at least enumerated, for consumers? >+ getClassObject : function mod_gch(compMgr, cid, iid) { "gch"? >Index: xpcom/io/nsLocalFileWin.cpp > NS_IMETHODIMP > nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval) > { > NS_ENSURE_ARG(inFile); > NS_ENSURE_ARG(_retval); > > nsCAutoString inFilePath; > inFile->GetNativePath(inFilePath); > >- *_retval = inFilePath.Equals(mWorkingPath); >+ *_retval = (_mbsicmp((unsigned char*) inFilePath.get(), >+ (unsigned char*) mWorkingPath.get()) == 0); > return NS_OK; > } I don't like this change; Windows can have case-sensitive filesystems, and changing the behaviour of our Equals method isn't something we should do lightly. If they match case-insensitively, _and_ they resolve to the same inode or Windows equivalent, I could maybe see us treating them as equal, but just throwing case-folding in there doesn't seem right. (And you still have the \-vs.-/ issue, I suspect.) r- on the basis of the case-sensitivity change.
Attachment #187969 - Flags: first-review?(shaver) → first-review-
Comment on attachment 187969 [details] [diff] [review] Part 1, rev. 1: make a nsIPersistentDescriptorService After more research, I've changed my mind: case-sensitivity will throw Windows into a tizzy anyway, and we will still be case preserving even with this change. r=shaver
Attachment #187969 - Flags: first-review-
Attachment #187969 - Flags: first-review+
Attachment #187969 - Flags: approval1.8b3?
Priority: -- → P1
Attachment #187969 - Flags: approval1.8b3? → approval1.8b3+
Comment on attachment 187969 [details] [diff] [review] Part 1, rev. 1: make a nsIPersistentDescriptorService I checked in only the nsLocalFileWin change, as the rest of this code is not being used yet.
Attachment #188692 - Flags: second-review?(darin)
Attachment #188692 - Flags: first-review?(rob_strong)
Attachment #188692 - Attachment description: Part 2, rev. 1: use the new relate/absolute descriptors and store the last-run-from path in compatibility.ini → Part 2, rev. 1: use the new relative/absolute descriptors and store the last-run-from path in compatibility.ini
Whiteboard: has patch, needs review robstrong+darin
Whiteboard: has patch, needs review robstrong+darin → [no l10n impact] has patch, needs review robstrong+darin
Why not just use relative descriptors for app-global extensions? Instead of making them relative to the profile directory, make them relative to the application directory. EM already supports uninstalling extensions that no longer exist, so this would seem to do the trick. It is probably just a few lines of code to implement this change in nsExtensionManager.js. Am I missing something? Sorry for not commenting sooner, but this is the first I've seen of this bug.
If we were only dealing with app and global it wouldn't be a big deal, but as we get more extension install paths (toolkit extensions) and have to deal with roaming profiles and USBstick-installations it starts to break down. This path solution works a lot better and is fairly robust.
I won't have time to do a proper review until around 7 PM PDT. What happens if the app path changes, the original app path contained an extension that is only compatible with 1.0+, and the new app path contains a newer version of the same extension that is only compatible with 1.1? Is the metadata updated so it isn't disabled at some point now that the extensions datasource is not deleted? - if (val) + if (val) { autoregFile.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE); + } else if (!autoregFile.exists()) nit: there's no reason for this change and it's not the style for the majority of this file. addItem: function(opType, entry) { - if (!(opType in this._ops)) - this._ops[opType] = []; - this._ops[opType].push(entry); + if (opType == "") { + delete this._ops[entry.id]; + } + else { + entry.opType = opType; + this._ops[entry.id] = entry; + } nit: I believe that opType == "" should be opType == OP_NONE
> If we were only dealing with app and global it wouldn't be a big deal, but as we > get more extension install paths (toolkit extensions) and have to deal with > roaming profiles and USBstick-installations it starts to break down. This path > solution works a lot better and is fairly robust. I don't understand why the extension install location type is not sufficient to select a base directory. You could have a different type for app global and toolkit global (heck call them app-global and toolkit-global). As for roaming profiles and USBstick-installations, can you describe the problem in more detail?
Another thing. If you want to use directory service keys, then you should really consider using resource:// URLs since unlike the component registry, EM has access to Necko.
Comment on attachment 188692 [details] [diff] [review] Part 2, rev. 1: use the new relative/absolute descriptors and store the last-run-from path in compatibility.ini Now that the extensions.rdf is not deleted this causes problems in _upgradeExtensionChrome and _upgradeThemeChrome when an item no longer exists in the extensions directory and the app path changes. Something along the lines of the following should resolve it. Just checking itemLocation won't work since getItemLocation creates the directory if it doesn't exist. for (i = 0; i < extensions.length; ++i) { var e = extensions[i]; var itemLocation = e.location.getItemLocation(e.id); + var installRDF = itemLocation.clone(); + installRDF.append(FILE_INSTALL_MANIFEST); + if (!installRDF.exists()) { + var installLocation = this.getInstallLocation(e.id); + StartupCache.put(installLocation, e.id, OP_NEEDS_UNINSTALL, true); + StartupCache.write(); + continue; + } var manifest = itemLocation.clone(); manifest.append(FILE_CHROME_MANIFEST);
Attachment #188692 - Flags: first-review?(rob_strong) → first-review-
The extensions directory i was referring to is the app's extensions directory (e.g. app-global).
I just noticed that it leaves the itemLocation behind so that will also need to be removed.
Attachment #188692 - Flags: second-review?(darin)
Benjamin and I discussed this over IRC, and we agreed that deleting extensions-startup.manifest from nsAppRunner is a bad idea because it records state information such as "needs-install" that could be lost. We also talked about the fact that file paths with spaces in them could be problematic.
This version does not remove extensions-startup.manifest on a path-change/upgrade, and because I am changing the format of exentensions-startup I have renamed it to extensions.cache.
Attachment #188692 - Attachment is obsolete: true
Attachment #189182 - Attachment is obsolete: true
Attachment #189197 - Flags: second-review?(darin)
Attachment #189197 - Flags: first-review?(rob_strong)
I tried out the patch and with a new or an existing profile all extensions were repeatedly installed during each startup until nsAppRunner prevented the restart. This includes subsequent restarts. I'll take a look at what could be causing this later tonight.
Here is where it is failing for me with setRelativeDescriptor requiring two args + if (m[1] == "rel") { + location.setRelativeDescriptor(decodeURI(installLocation.location, m[2])); + }
Comment on attachment 189197 [details] [diff] [review] Part 2, rev. 2.1: use relative/absolute descriptors and store the last-run-from path in compatibility.ini >-const FILE_INSTALLED_EXTENSIONS = "installed-extensions.txt" >-const FILE_INSTALLED_EXTENSIONS_PROCESSED = "installed-extensions-processed.txt" There are several more constants that can be removed if you are willing. :) DIR_DEFAULTS_PREFS DIR_UNINSTALL DIR_COMPONENTS DIR_DEFAULTS DIR_DEFAULTS_PREFS DIR_DEFAULTS_EXTENSIONS >- * @param locationKey >- * The name of the Install Location where the item is installed > * @param descriptor > * The descriptor that locates the directory > * @returns The nsILocalFile object representing the location of the item > */ >-function getFileFromDescriptor(locationKey, descriptor) { <snip> >+function getFileFromDescriptor(descriptor, installLocation) { Needs a @param installLocation description >- var lf = Components.classes["@mozilla.org/file/local;1"] >- .createInstance(nsILocalFile); nsILocalFile is declared as a constant and the style should be var location = Components.classes["@mozilla.org/file/local;1"] .createInstance(nsILocalFile); >+ if (m[1] == "rel") { >+ location.setRelativeDescriptor(decodeURI(installLocation.location, m[2])); >+ } >+ else { >+ location.persistentDescriptor = decodeURI(m[2]); >+ } setRelativeDescriptor requires two args with a nsILocalFile for the first arg. What requirement is there to use encodeURI and decodeURI? >+ var line = locationKey + "\t" + id + "\t" + entry.descriptor + "\t" + >+ itemMTime + "\t" + entry.op + "\r\n"; >+ fos.write(line, line.length); >+ } >+ catch (e) { } Might be a good idea to LOG the error here. >+ var installRDF = itemLocation.clone(); >+ installRDF.append(FILE_INSTALL_MANIFEST); >+ if (!installRDF.exists()) { >+ var installLocation = this.getInstallLocation(e.id); >+ StartupCache.put(installLocation, e.id, OP_NEEDS_UNINSTALL, true); >+ StartupCache.write(); >+ continue; >+ } This does not appear to be needed anymore now that the extension's cache file is not deleted. I don't believe it will cause any harm and it may in some instances prevent and / or hide problems. >+ var installRDF = itemLocation.clone(); >+ installRDF.append(FILE_INSTALL_MANIFEST); >+ if (!installRDF.exists()) { >+ var installLocation = this.getInstallLocation(item.id); >+ StartupCache.put(installLocation, item.id, OP_NEEDS_UNINSTALL, true); >+ StartupCache.write(); >+ continue; >+ } Same here >- if (val) >+ if (val) { > autoregFile.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE); >+ } > else if (!autoregFile.exists()) nit: the added braces aren't the style of the majority of the file
Attachment #189197 - Flags: first-review?(rob_strong) → first-review-
Attachment #189182 - Flags: first-review?(rob_strong)
(In reply to comment #16) > ... We also talked > about the fact that file paths with spaces in them could be problematic. I should have done my homework better. The code uses tabs instead of spaces as delimiters in extensions-startup.manifest, so there is no concern about filenames that contain spaces.
Comment on attachment 189197 [details] [diff] [review] Part 2, rev. 2.1: use relative/absolute descriptors and store the last-run-from path in compatibility.ini The URIencode/decode stuff was added at the last second without proper testing: since the manifest is tab-delimited anyway I will remove that code.
Attachment #189197 - Flags: second-review?(darin)
Comment on attachment 189326 [details] [diff] [review] Part 2, rev. 2.2: use relative/absolute descriptors and store the last-run-from path in compatibility.ini (no encodeURI/decodeURI) brilliant
Attachment #189326 - Flags: first-review?(rob_strong) → first-review+
Whiteboard: [no l10n impact] has patch, needs review robstrong+darin → [no l10n impact] has patch, needs review darin
Comment on attachment 189326 [details] [diff] [review] Part 2, rev. 2.2: use relative/absolute descriptors and store the last-run-from path in compatibility.ini (no encodeURI/decodeURI) >Index: toolkit/mozapps/extensions/src/nsExtensionManager.js.in >-function getDescriptorFromFile(locationKey, itemLocation) { >+function getRelativeDescriptor(itemLocation, installLocation) { I still prefer the previous name since the descriptor is not always relative. It also goes well with getFileFromDescriptor. >+function getFileFromDescriptor(descriptor, installLocation) { >+ var location = Components.classes["@mozilla.org/file/local;1"] >+ .createInstance(nsILocalFile); >+ >+ var m = descriptor.match(/^(abs|rel)\%(.*)$/); >+ if (!m) >+ throw Components.results.NS_ERROR_INVALID_ARG; >+ >+ if (m[1] == "rel") { >+ location.setRelativeDescriptor(installLocation.location, m[2]); >+ } >+ else { >+ location.persistentDescriptor = m[2]; >+ } >+ >+ return location; > } This function only works if descriptor was generated by getRelativeDescriptor right? It doesn't work if given a raw persistentDescriptor, right? >Index: toolkit/xre/nsAppRunner.cpp >+CompatibilityCheck(nsIFile* aProfileDir, const nsCString& aVersion, >+ nsIFile* aXULRunnerDir, nsIFile* aAppDir) DoCompatibilityCheck? or maybe CheckCompatibility? either one of those seems like a better name to me. i prefer the second. >Index: toolkit/xre/nsXULAppAPI.h >+ * A directory service key which provides the platform-correct >+ * "application data" directory. >+ * Windows: Documents and Settings\<User>\Application Data\<Vendor>\<Application> >+ * Unix: ~/.<vendor>/<application> >+ * Mac: ~/Library/Application Supports/<Application> >+ */ >+#define XRE_USER_APP_DATA_DIR "UAppData" So, I don't see any consumer of this key in this patch. Do you really mean to add it? Or, is it leftover from a previous version of this patch? >Index: xpcom/io/nsLocalFileWin.cpp > NS_IMETHODIMP > nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval) ... >+ DWORD thisr = GetShortPathName(mWorkingPath.get(), thisshort, sizeof(thisshort)); >+ DWORD thatr = GetShortPathName(inFilePath.get(), thatshort, sizeof(thatshort)); It'll be interesting to see what kind of impact if any this may have on performance.
Attachment #189326 - Flags: second-review?(darin) → second-review+
Attached patch Part 2, rev 2.3 (final) (deleted) — Splinter Review
This is the final patch for checkin, self-approving per deerpark triage.
Attachment #187969 - Attachment is obsolete: true
Attachment #189197 - Attachment is obsolete: true
Attachment #189326 - Attachment is obsolete: true
Attachment #190246 - Flags: approval1.8b4+
Fixed on trunk for 1.8b4
Status: NEW → RESOLVED
Closed: 19 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla1.8beta4
> >+#define XRE_USER_APP_DATA_DIR "UAppData" > > So, I don't see any consumer of this key in this patch. Do > you really mean to add it? Or, is it leftover from a previous > version of this patch? I did add it for a previous revision, but I still think it's a good idea... people have been using APP_REGISTRY_DIR for this, but APP_REGISTRY_DIR may (will) change in the future.
*** Bug 301135 has been marked as a duplicate of this bug. ***
This bug might have caused a memory leak. It's either this, or bug 300731. Before: RLk:4.55KB Lk:300KB MH:7.69MB A:215K After: RLk:4.55KB Lk:338KB <-- MH:8.15MB <-- A:249K <---
I have no clue if it has to do with this bug, but I see a terrible memory leak too. I surfed for an hour with only two tabs open, no java, no flash, and suddenly I could hardly scroll. The browser scrolled laggy and with delay on a normal site like bugzilla. There was only 32 MB RAM left and the taskmanager told me that Firefox was using 146.200 kB.
*** Bug 301965 has been marked as a duplicate of this bug. ***
Component: XRE Startup → Startup and Profile System
QA Contact: nobody → startup
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: