/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //------------------------------------------------------------------------ #include "nsIFile.h" #include "mozilla/ModuleUtils.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsDirectoryServiceDefs.h" #include "nsLiteralString.h" #include "nsReadableUtils.h" #include "nsIStringBundle.h" #include "mozilla/Services.h" #define INCL_WIN #define INCL_DOS #include // nsRwsService must be included _after_ os2.h #include "nsRwsService.h" #include "rwserr.h" #include "nsOS2Uni.h" #include "prenv.h" #include //------------------------------------------------------------------------ // debug macros //------------------------------------------------------------------------ #ifdef DEBUG #define RWS_DEBUG #endif // use this to force debug msgs in non-debug builds //#ifndef RWS_DEBUG // #define RWS_DEBUG //#endif #ifdef RWS_DEBUG #define ERRMSG(x,y) { printf(y " failed - rc= %d\n", (int)x); } #define ERRBREAK(x,y) if (x) { ERRMSG(x,y); break; } #define ERRPRINTF(x,y) { printf(y "\n", x); } #else #define ERRMSG(x,y) ; #define ERRBREAK(x,y) if (x) break; #define ERRPRINTF(x,y) ; #endif //------------------------------------------------------------------------ // function prototypes //------------------------------------------------------------------------ static nsresult IsDescendedFrom(uint32_t wpsFilePtr, const char *pszClassname); static nsresult CreateFileForExtension(const char *aFileExt, nsACString& aPath); static nsresult DeleteFileForExtension(const char *aPath); static void AssignNLSString(const PRUnichar *aKey, nsAString& _retval); static nsresult AssignTitleString(const char *aTitle, nsAString& result); //------------------------------------------------------------------------ // module-level variables - not declared as members of nsRws //------------------------------------------------------------------------ static nsRwsService *sRwsInstance = 0; // our singleton instance static bool sInit = FALSE; // have we tried to load RWS // RWS administrative functions static ULONG (* _System sRwsClientInit)(BOOL); static ULONG (* _System sRwsGetTimeout)(PULONG, PULONG); static ULONG (* _System sRwsSetTimeout)(ULONG); static ULONG (* _System sRwsClientTerminate)(void); // RWS task-oriented functions static ULONG (* _System sRwsCall)(PRWSHDR*, ULONG, ULONG, ULONG, ULONG, ULONG, ...); static ULONG (* _System sRwsCallIndirect)(PRWSBLD); static ULONG (* _System sRwsFreeMem)(PRWSHDR); static ULONG (* _System sRwsGetResult)(PRWSHDR, ULONG, PULONG); static ULONG (* _System sRwsGetArgPtr)(PRWSHDR, ULONG, PRWSDSC*); //------------------------------------------------------------------------ // ExtCache - caches the default icons & handlers for file extensions //------------------------------------------------------------------------ typedef struct _ExtInfo { char ext[8]; uint32_t icon; uint32_t mini; uint32_t handler; PRUnichar *title; } ExtInfo; #define kGrowBy 8 #define kMutexTimeout 500 class ExtCache { public: ExtCache(); ~ExtCache(); nsresult GetIcon(const char *aExt, bool aNeedMini, uint32_t *oIcon); nsresult SetIcon(const char *aExt, bool aIsMini, uint32_t aIcon); nsresult GetHandler(const char *aExt, uint32_t *oHandle, nsAString& oTitle); nsresult SetHandler(const char *aExt, uint32_t aHandle, nsAString& aTitle); void EmptyCache(); protected: ExtInfo *FindExtension(const char *aExt, bool aSet = false); uint32_t mPid; uint32_t mMutex; uint32_t mCount; uint32_t mSize; ExtInfo *mExtInfo; }; //------------------------------------------------------------------------ // nsIRwsService implementation //------------------------------------------------------------------------ NS_IMPL_ISUPPORTS2(nsRwsService, nsIRwsService, nsIObserver) nsRwsService::nsRwsService() { mExtCache = new ExtCache(); if (!mExtCache) ERRPRINTF("", "nsRwsService - unable to allocate mExtArray%s"); } nsRwsService::~nsRwsService() { } //------------------------------------------------------------------------ // provides the default icon associated with a given extension; if the // icon isn't in the cache, it creates a temp file with that extension, // retrieves its icon, deletes the temp file, then caches the icon NS_IMETHODIMP nsRwsService::IconFromExtension(const char *aExt, bool aNeedMini, uint32_t *_retval) { if (!aExt || !*aExt || !_retval) return NS_ERROR_INVALID_ARG; nsresult rv = mExtCache->GetIcon(aExt, aNeedMini, _retval); if (NS_SUCCEEDED(rv)) return rv; nsAutoCString path; rv = CreateFileForExtension(aExt, path); if (NS_SUCCEEDED(rv)) { rv = IconFromPath(path.get(), false, aNeedMini, _retval); DeleteFileForExtension(path.get()); if (NS_SUCCEEDED(rv)) mExtCache->SetIcon(aExt, aNeedMini, *_retval); } return rv; } //------------------------------------------------------------------------ // retrieves an object's icon using its fully-qualified path; aAbstract // indicates whether this is a Filesystem object or an Abstract or // Transient object; locating non-file objects is fairly expensive NS_IMETHODIMP nsRwsService::IconFromPath(const char *aPath, bool aAbstract, bool aNeedMini, uint32_t *_retval) { if (!aPath || !*aPath || !_retval) return NS_ERROR_INVALID_ARG; uint32_t rwsType; if (aAbstract) rwsType = (aNeedMini ? RWSC_OFTITLE_OMINI : RWSC_OFTITLE_OICON); else rwsType = (aNeedMini ? RWSC_OPATH_OMINI : RWSC_OPATH_OICON); return RwsConvert(rwsType, (uint32_t)aPath, _retval); } //------------------------------------------------------------------------ // retrieve's an object's icon using its persistent handle NS_IMETHODIMP nsRwsService::IconFromHandle(uint32_t aHandle, bool aNeedMini, uint32_t *_retval) { if (!aHandle || !_retval) return NS_ERROR_INVALID_ARG; return RwsConvert( (aNeedMini ? RWSC_OHNDL_OMINI : RWSC_OHNDL_OICON), aHandle, _retval); } //------------------------------------------------------------------------ // retrieve's an object's title using its persistent handle NS_IMETHODIMP nsRwsService::TitleFromHandle(uint32_t aHandle, nsAString& _retval) { if (!aHandle) return NS_ERROR_INVALID_ARG; return RwsConvert(RWSC_OHNDL_OTITLE, aHandle, _retval); } //------------------------------------------------------------------------ // provides the default handler associated with a given extension; if the // info isn't in the cache, it creates a temp file with that extension, // retrieves the handler's title & possibly its object handle, deletes the // temp file, then caches the info. NS_IMETHODIMP nsRwsService::HandlerFromExtension(const char *aExt, uint32_t *aHandle, nsAString& _retval) { if (!aExt || !*aExt || !aHandle) return NS_ERROR_INVALID_ARG; nsresult rv = mExtCache->GetHandler(aExt, aHandle, _retval); if (NS_SUCCEEDED(rv)) return rv; nsAutoCString path; rv = CreateFileForExtension(aExt, path); if (NS_SUCCEEDED(rv)) { rv = HandlerFromPath(path.get(), aHandle, _retval); DeleteFileForExtension(path.get()); if (NS_SUCCEEDED(rv)) mExtCache->SetHandler(aExt, *aHandle, _retval); } return rv; } //------------------------------------------------------------------------ // identifies the default WPS handler for a given file; if the handler // is an associated program, returns its title & object handle; if the // handler is an integrated class viewer or player, it constructs a title // and sets the object handle to zero NS_IMETHODIMP nsRwsService::HandlerFromPath(const char *aPath, uint32_t *aHandle, nsAString& _retval) { if (!aPath || !*aPath || !aHandle) return NS_ERROR_INVALID_ARG; nsresult rv = NS_ERROR_FAILURE; PRWSHDR pHdr = 0; uint32_t rc; _retval.Truncate(); *aHandle = 0; // this dummy do{...}while(0) loop lets us bail out early // while ensuring pHdr gets freed do { // get the default view for this file rc = sRwsCall(&pHdr, RWSP_MNAM, (ULONG)"wpQueryDefaultView", RWSR_ASIS, 0, 1, RWSI_OPATH, 0, (ULONG)aPath); ERRBREAK(rc, "wpQueryDefaultView") uint32_t defView = sRwsGetResult(pHdr, 0, 0); if (defView == (uint32_t)-1) break; // if the default view is OPEN_SETTINGS ('2'), // change it to OPEN_RUNNING ('4') if (defView == 2) defView = 4; // to improve efficiency, retrieve & reuse the file's wpObject // ptr rather than repeatedly converting the name to an object uint32_t wpsFilePtr = sRwsGetResult(pHdr, 1, 0); // free pHdr before the next call sRwsFreeMem(pHdr); pHdr = 0; // for default view values less than OPEN_USER, see if there // is a program or program object associated with this file if (defView < 0x6500) { rc = sRwsCall(&pHdr, RWSP_MNAM, (ULONG)"wpQueryAssociatedProgram", RWSR_OHNDL, 0, 6, RWSI_ASIS, 0, wpsFilePtr, RWSI_ASIS, 0, defView, RWSI_PULONG, 0, 0, RWSI_PBUF, 32, 0, RWSI_ASIS, 0, 32, RWSI_ASIS, 0, (ULONG)-1); // if there's no associated program, create dummy values // (if chosen, the WPS will open the file's Properties notebook) if (rc) { if (rc == RWSSRV_FUNCTIONFAILED) { *aHandle = 0; AssignNLSString(NS_LITERAL_STRING("wpsDefaultOS2").get(), _retval); rv = NS_OK; } else ERRMSG(rc, "wpQueryAssociatedProgram") break; } // get a ptr to the return value's data structure; then get // both the object handle we requested & the raw WPS object ptr PRWSDSC pRtn; rc = sRwsGetArgPtr(pHdr, 0, &pRtn); ERRBREAK(rc, "GetArgPtr") *aHandle = *((uint32_t*)pRtn->pget); uint32_t wpsPgmPtr = pRtn->value; // free pHdr before the next call sRwsFreeMem(pHdr); pHdr = 0; // convert the object to its title (using Rws' conversion // feature is more efficient than calling wpQueryTitle) rc = sRwsCall(&pHdr, RWSP_CONV, 0, RWSR_ASIS, 0, 1, RWSC_OBJ_OTITLE, 0, wpsPgmPtr); ERRBREAK(rc, "convert pgm object to title") // retrieve the title, massage it & assign to _retval, then exit // N.B. no need to free pHdr here, it will be freed below char *pszTitle = (char*)sRwsGetResult(pHdr, 1, 0); if (pszTitle != (char*)-1) rv = AssignTitleString(pszTitle, _retval); break; } // the default view is provided by the file's WPS class; // in this case, *aHandle is 0 // see if it's a known view that can be given a meaningful name switch (defView) { case 0xbc2b: { rv = IsDescendedFrom(wpsFilePtr, "MMImage"); if (NS_SUCCEEDED(rv)) AssignNLSString(NS_LITERAL_STRING("mmImageViewerOS2").get(), _retval); break; } case 0xbc0d: // Audio editor case 0xbc21: // Video editor case 0xbc17: // Midi editor case 0xbbef: // Play case 0xbbe5: { // Player rv = IsDescendedFrom(wpsFilePtr, "MMAudio"); if (NS_SUCCEEDED(rv)) { AssignNLSString(NS_LITERAL_STRING("mmAudioPlayerOS2").get(), _retval); break; } rv = IsDescendedFrom(wpsFilePtr, "MMVideo"); if (NS_SUCCEEDED(rv)) { AssignNLSString(NS_LITERAL_STRING("mmVideoPlayerOS2").get(), _retval); break; } rv = IsDescendedFrom(wpsFilePtr, "MMMIDI"); if (NS_SUCCEEDED(rv)) AssignNLSString(NS_LITERAL_STRING("mmMidiPlayerOS2").get(), _retval); break; } case 0x7701: { rv = IsDescendedFrom(wpsFilePtr, "TSArcMgr"); if (NS_SUCCEEDED(rv)) AssignNLSString(NS_LITERAL_STRING("odZipFolderOS2").get(), _retval); break; } // this has to go last because TSEnhDataFile replaces // WPDataFile; if present, all data files are descended from it case 0xb742: case 0xa742: { rv = IsDescendedFrom(wpsFilePtr, "TSEnhDataFile"); if (NS_SUCCEEDED(rv)) AssignNLSString(NS_LITERAL_STRING("odTextViewOS2").get(), _retval); break; } } // end switch if (NS_SUCCEEDED(rv)) break; // the name of this view is unknown, so create a generic name // (i.e. "Viewer for Class WPSomething") // use the file's object ptr to get the name of its class rc = sRwsCall(&pHdr, RWSP_CONV, 0, RWSR_ASIS, 0, 1, RWSC_OBJ_CNAME, 0, wpsFilePtr); ERRBREAK(rc, "convert object to classname") char *pszTitle = (char*)sRwsGetResult(pHdr, 1, 0); if (pszTitle == (char*)-1) break; nsAutoChar16Buffer buffer; int32_t bufLength; rv = MultiByteToWideChar(0, pszTitle, strlen(pszTitle), buffer, bufLength); if (NS_FAILED(rv)) break; nsAutoString classViewer; AssignNLSString(NS_LITERAL_STRING("classViewerOS2").get(), classViewer); int pos = -1; if ((pos = classViewer.Find("%S")) > -1) classViewer.Replace(pos, 2, buffer.Elements()); _retval.Assign(classViewer); rv = NS_OK; } while (0); // free the pHdr allocated by the final call sRwsFreeMem(pHdr); return rv; } //------------------------------------------------------------------------ // retrieves an object's menu using its fully-qualified path; aAbstract // indicates whether this is a Filesystem object or an Abstract or // Transient object; locating non-file objects is fairly expensive NS_IMETHODIMP nsRwsService::MenuFromPath(const char *aPath, bool aAbstract) { if (!aPath || !*aPath) return NS_ERROR_INVALID_ARG; nsresult rv = NS_ERROR_FAILURE; PRWSHDR pHdr = 0; uint32_t type = (aAbstract ? RWSI_OFTITLE : RWSI_OPATH); uint32_t rc; POINTL ptl; HWND hTgt = 0; // try to identify the window where the click occurred if (WinQueryMsgPos(0, &ptl)) { hTgt = WinQueryFocus(HWND_DESKTOP); if (hTgt) WinMapWindowPoints(HWND_DESKTOP, hTgt, &ptl, 1); } // if we have the window & coordinates, use them; otherwise, // let RWS position the menu at the current pointer position // (specifying the window ensures the focus will return to it) if (hTgt) rc = sRwsCall(&pHdr, RWSP_CMD, RWSCMD_POPUPMENU, RWSR_ASIS, 0, 3, type, 0, (ULONG)aPath, RWSI_ASIS, 0, hTgt, RWSI_PBUF, sizeof(POINTL), (ULONG)&ptl); else rc = sRwsCall(&pHdr, RWSP_CMD, RWSCMD_POPUPMENU, RWSR_ASIS, 0, 3, type, 0, (ULONG)aPath, RWSI_ASIS, 0, 0, RWSI_ASIS, 0, 0); if (rc) ERRMSG(rc, "RWSCMD_POPUPMENU") else rv = NS_OK; // free pHdr sRwsFreeMem(pHdr); return rv; } //------------------------------------------------------------------------ // causes the WPS to open a file using the program specified by AppPath; // this is identical to dropping the file on the program's WPS icon NS_IMETHODIMP nsRwsService::OpenWithAppHandle(const char *aFilePath, uint32_t aAppHandle) { if (!aFilePath || !*aFilePath || !aAppHandle) return NS_ERROR_INVALID_ARG; nsresult rv = NS_ERROR_FAILURE; PRWSHDR pHdr = 0; uint32_t rc; rc = sRwsCall(&pHdr, RWSP_CMD, RWSCMD_OPENUSING, RWSR_ASIS, 0, 2, RWSI_OPATH, 0, aFilePath, RWSI_OHNDL, 0, aAppHandle); if (rc) ERRMSG(rc, "RWSCMD_OPENUSING") else rv = NS_OK; sRwsFreeMem(pHdr); return rv; } //------------------------------------------------------------------------ // causes the WPS to open a file using the program specified by AppPath; // this is identical to dropping the file on the program's WPS icon NS_IMETHODIMP nsRwsService::OpenWithAppPath(const char *aFilePath, const char *aAppPath) { if (!aFilePath || !*aFilePath || !aAppPath || !*aAppPath) return NS_ERROR_INVALID_ARG; nsresult rv = NS_ERROR_FAILURE; PRWSHDR pHdr = 0; uint32_t rc; rc = sRwsCall(&pHdr, RWSP_CMD, RWSCMD_OPENUSING, RWSR_ASIS, 0, 2, RWSI_OPATH, 0, aFilePath, RWSI_OPATH, 0, aAppPath); if (rc) ERRMSG(rc, "RWSCMD_OPENUSING") else rv = NS_OK; sRwsFreeMem(pHdr); return rv; } //------------------------------------------------------------------------ // nsRwsService additional methods //------------------------------------------------------------------------ // uses RwsConvert to retrieve a result that can be handled as a ULONG; // type identifies the conversion, value can be anything (ULONG, char*, etc) //static nsresult nsRwsService::RwsConvert(uint32_t type, uint32_t value, uint32_t *result) { nsresult rv = NS_ERROR_FAILURE; PRWSHDR pHdr = 0; *result = 0; uint32_t rc = sRwsCall(&pHdr, RWSP_CONV, 0, RWSR_ASIS, 0, 1, type, 0, value); if (rc) ERRMSG(rc, "RwsConvert to ULONG") else { *result = sRwsGetResult(pHdr, 1, 0); if (*result == (uint32_t)-1) *result = 0; else rv = NS_OK; } sRwsFreeMem(pHdr); return rv; } //------------------------------------------------------------------------ // uses RwsConvert to retrieve a result that can be handled as a // Unicode string; type identifies the conversion, value can be // anything (ULONG, char*, etc) //static nsresult nsRwsService::RwsConvert(uint32_t type, uint32_t value, nsAString& result) { nsresult rv = NS_ERROR_FAILURE; PRWSHDR pHdr = 0; result.Truncate(); uint32_t rc = sRwsCall(&pHdr, RWSP_CONV, 0, RWSR_ASIS, 0, 1, type, 0, value); if (rc) ERRMSG(rc, "RwsConvert to string") else { char *string = (char*)sRwsGetResult(pHdr, 1, 0); if (string != (char*)-1) rv = AssignTitleString(string, result); } sRwsFreeMem(pHdr); return rv; } //------------------------------------------------------------------------ // nsIObserver implementation //------------------------------------------------------------------------ // when the app shuts down, advise RwsClient; if this is the // last app using RwsServer, the WPS will unload the class's dll; // also, empty the extension cache to unlock & delete its icons NS_IMETHODIMP nsRwsService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aSomeData) { if (strcmp(aTopic, "quit-application") == 0) { uint32_t rc = sRwsClientTerminate(); if (rc) ERRMSG(rc, "RwsClientTerminate"); if (mExtCache) mExtCache->EmptyCache(); } return NS_OK; } //------------------------------------------------------------------------ // nsRwsService static helper functions //------------------------------------------------------------------------ // this wrapper for somIsA() makes HandlerFromPath() easier to read static nsresult IsDescendedFrom(uint32_t wpsFilePtr, const char *pszClassname) { PRWSHDR pHdr = 0; nsresult rv = NS_ERROR_FAILURE; uint32_t rc = sRwsCall(&pHdr, RWSP_MNAMI, (ULONG)"somIsA", RWSR_ASIS, 0, 2, RWSI_ASIS, 0, wpsFilePtr, RWSI_CNAME, 0, pszClassname); if (rc) ERRMSG(rc, "somIsA") else if (sRwsGetResult(pHdr, 0, 0) == TRUE) rv = NS_OK; sRwsFreeMem(pHdr); return rv; } //------------------------------------------------------------------------ // create a temp file with the specified extension, then return its path static nsresult CreateFileForExtension(const char *aFileExt, nsACString& aPath) { nsresult rv = NS_ERROR_FAILURE; aPath.Truncate(); nsCOMPtr tempFile; rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempFile)); if (NS_FAILED(rv)) return rv; nsAutoCString pathStr(NS_LITERAL_CSTRING("nsrws.")); if (*aFileExt == '.') aFileExt++; pathStr.Append(aFileExt); rv = tempFile->AppendNative(pathStr); if (NS_FAILED(rv)) return rv; tempFile->GetNativePath(pathStr); FILE *fp = fopen(pathStr.get(), "wb+"); if (fp) { fclose(fp); aPath.Assign(pathStr); rv = NS_OK; } return rv; } //------------------------------------------------------------------------ // delete a temp file created earlier static nsresult DeleteFileForExtension(const char *aPath) { if (!aPath || !*aPath) return NS_ERROR_INVALID_ARG; remove(aPath); return NS_OK; } //------------------------------------------------------------------------ // returns a localized string from unknownContentType.properties; // if there's a failure, returns "WPS Default" static void AssignNLSString(const PRUnichar *aKey, nsAString& result) { nsresult rv; nsXPIDLString title; do { nsCOMPtr bundleSvc = mozilla::services::GetStringBundleService(); if (!bundleSvc) break; nsCOMPtr bundle; rv = bundleSvc->CreateBundle( "chrome://mozapps/locale/downloads/unknownContentType.properties", getter_AddRefs(bundle)); if (NS_FAILED(rv)) break; // if we can't fetch the requested string, try to get "WPS Default" rv = bundle->GetStringFromName(aKey, getter_Copies(title)); if (NS_FAILED(rv)) rv = bundle->GetStringFromName(NS_LITERAL_STRING("wpsDefaultOS2").get(), getter_Copies(title)); } while (0); if (NS_SUCCEEDED(rv)) result.Assign(title); else result.Assign(NS_LITERAL_STRING("WPS Default")); } //------------------------------------------------------------------------ // converts a native string (presumably a WPS object's title) to // Unicode, removes line breaks, and compresses whitespace static nsresult AssignTitleString(const char *aTitle, nsAString& result) { nsAutoChar16Buffer buffer; int32_t bufLength; // convert the title to Unicode if (NS_FAILED(MultiByteToWideChar(0, aTitle, strlen(aTitle), buffer, bufLength))) return NS_ERROR_FAILURE; PRUnichar *pSrc; PRUnichar *pDst; bool fSkip; // remove line breaks, leading whitespace, & extra embedded whitespace // (primitive, but gcc 3.2.2 doesn't support wchar) for (fSkip=true, pSrc=pDst=buffer.Elements(); *pSrc; pSrc++) { if (*pSrc == ' ' || *pSrc == '\r' || *pSrc == '\n' || *pSrc == '\t') { if (!fSkip) *pDst++ = ' '; fSkip = true; } else { if (pDst != pSrc) *pDst = *pSrc; pDst++; fSkip = false; } } // remove the single trailing space, if needed if (fSkip && pDst > buffer.Elements()) pDst--; *pDst = 0; result.Assign(buffer.Elements()); return NS_OK; } //------------------------------------------------------------------------ // ExtCache implementation //------------------------------------------------------------------------ ExtCache::ExtCache() : mCount(0), mSize(0), mExtInfo(0) { PTIB ptib; PPIB ppib; // the PID is needed to lock/unlock icons DosGetInfoBlocks(&ptib, &ppib); mPid = ppib->pib_ulpid; uint32_t rc = DosCreateMutexSem(0, (PHMTX)&mMutex, 0, 0); if (rc) ERRMSG(rc, "DosCreateMutexSem") } ExtCache::~ExtCache() {} //------------------------------------------------------------------------ // retrieve the WPS's default icon for files with this extension nsresult ExtCache::GetIcon(const char *aExt, bool aNeedMini, uint32_t *oIcon) { uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout); if (rc) { ERRMSG(rc, "DosRequestMutexSem") return NS_ERROR_FAILURE; } ExtInfo *info = FindExtension(aExt); if (info) { if (aNeedMini) *oIcon = info->mini; else *oIcon = info->icon; } else *oIcon = 0; rc = DosReleaseMutexSem(mMutex); if (rc) ERRMSG(rc, "DosReleaseMutexSem") return (*oIcon ? NS_OK : NS_ERROR_FAILURE); } //------------------------------------------------------------------------ // save the WPS's default icon for files with this extension nsresult ExtCache::SetIcon(const char *aExt, bool aIsMini, uint32_t aIcon) { uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout); if (rc) { ERRMSG(rc, "DosRequestMutexSem") return NS_ERROR_FAILURE; } ExtInfo *info = FindExtension(aExt, true); if (!info) return NS_ERROR_FAILURE; // the icon has to be made non-deletable or else // it will be destroyed if the WPS terminates if (!WinSetPointerOwner(aIcon, mPid, FALSE)) { ERRPRINTF(info->ext, "WinSetPointerOwner failed for %s icon") return NS_ERROR_FAILURE; } if (aIsMini) info->mini = aIcon; else info->icon = aIcon; ERRPRINTF(info->ext, "ExtCache - added icon for %s") rc = DosReleaseMutexSem(mMutex); if (rc) ERRMSG(rc, "DosReleaseMutexSem") return NS_OK; } //------------------------------------------------------------------------ // retrieve the WPS default handler's title & object handle (if any) nsresult ExtCache::GetHandler(const char *aExt, uint32_t *oHandle, nsAString& oTitle) { uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout); if (rc) { ERRMSG(rc, "DosRequestMutexSem") return NS_ERROR_FAILURE; } nsresult rv = NS_ERROR_FAILURE; ExtInfo *info = FindExtension(aExt); // if there's no title, the handle isn't useful if (info && info->title) { oTitle.Assign(info->title); *oHandle = info->handler; rv = NS_OK; } rc = DosReleaseMutexSem(mMutex); if (rc) ERRMSG(rc, "DosReleaseMutexSem") return rv; } //------------------------------------------------------------------------ // save the WPS default handler's title & object handle (if any) nsresult ExtCache::SetHandler(const char *aExt, uint32_t aHandle, nsAString& aTitle) { uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout); if (rc) { ERRMSG(rc, "DosRequestMutexSem") return NS_ERROR_FAILURE; } nsresult rv = NS_ERROR_FAILURE; ExtInfo *info = FindExtension(aExt, true); // if the title can't be saved, don't save the handle if (info) { info->title = ToNewUnicode(aTitle); if (info->title) { info->handler = aHandle; rv = NS_OK; ERRPRINTF(info->ext, "ExtCache - added handler for %s") } } rc = DosReleaseMutexSem(mMutex); if (rc) ERRMSG(rc, "DosReleaseMutexSem") return rv; } //------------------------------------------------------------------------ // find the entry for the requested extension; if not found, // create a new entry, expanding the array as needed ExtInfo *ExtCache::FindExtension(const char *aExt, bool aSet) { // eliminate any leading dot & null extensions if (*aExt == '.') aExt++; if (*aExt == 0) return 0; // too long to cache if (strlen(aExt) >= 8) return 0; // uppercase extension, then confirm it still fits char extUpper[16]; strcpy(extUpper, aExt); // XXX WinUpper() can crash with high-memory // could we just store as-is and instead use stricmp() for // compare operations? if (WinUpper(0, 0, 0, extUpper) >= 8) return 0; // a minor optimization: if we're setting a value, it probably // belongs to the entry added most recently (i.e. the last one) if (aSet && mCount && !strcmp(extUpper, (&mExtInfo[mCount-1])->ext)) return &mExtInfo[mCount-1]; ExtInfo *info; uint32_t ctr; // look for the extension in the array, return if found for (ctr = 0, info = mExtInfo; ctr < mCount; ctr++, info++) if (!strcmp(extUpper, info->ext)) return info; // if a new entry won't fit into the current array, realloc if (mCount >= mSize) { uint32_t newSize = mSize + kGrowBy; info = (ExtInfo*) NS_Realloc(mExtInfo, newSize * sizeof(ExtInfo)); if (!info) return 0; memset(&info[mSize], 0, kGrowBy * sizeof(ExtInfo)); mExtInfo = info; mSize = newSize; } // point at the next entry & store the extension info = &mExtInfo[mCount++]; strcpy(info->ext, extUpper); return info; } //------------------------------------------------------------------------ // clear out the cache - since this is only called at shutdown, // the primary concern is that the icons get unlocked & deleted void ExtCache::EmptyCache() { if (!mExtInfo) return; uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout); if (rc) { ERRMSG(rc, "DosRequestMutexSem") return; } uint32_t saveMutex = mMutex; mMutex = 0; uint32_t ctr; ExtInfo *info; for (ctr = 0, info = mExtInfo; ctr < mCount; ctr++, info++) { ERRPRINTF(info->ext, "ExtCache - deleting entry for %s") if (info->icon) { if (WinSetPointerOwner(info->icon, mPid, TRUE) == FALSE || WinDestroyPointer(info->icon) == FALSE) ERRPRINTF(info->ext, "unable to destroy icon for %s") } if (info->mini) { if (WinSetPointerOwner(info->mini, mPid, TRUE) == FALSE || WinDestroyPointer(info->mini) == FALSE) ERRPRINTF(info->ext, "unable to destroy mini for %s") } if (info->title) NS_Free(info->title); } mCount = 0; mSize = 0; NS_Free(mExtInfo); mExtInfo = 0; rc = DosReleaseMutexSem(saveMutex); if (rc) ERRMSG(rc, "DosReleaseMutexSem") rc = DosCloseMutexSem(saveMutex); if (rc) ERRMSG(rc, "DosCloseMutexSem") } //------------------------------------------------------------------------ // Module & Factory stuff //------------------------------------------------------------------------ // this is the "getter proc" for nsRwsServiceConstructor(); it makes a // single attempt to load the RWS libraries and, if successful, creates // our singleton object; thereafter, it returns the existing object or // NS_ERROR_NOT_AVAILABLE static nsresult nsRwsServiceInit(nsRwsService **aClass) { // init already done - return what we've got or an error if (sInit) { *aClass = sRwsInstance; if (*aClass == 0) return NS_ERROR_NOT_AVAILABLE; NS_ADDREF(*aClass); return NS_OK; } sInit = TRUE; *aClass = 0; // don't load RWS if "MOZ_NO_RWS" is found in the environment if (PR_GetEnv("MOZ_NO_RWS")) return NS_ERROR_NOT_AVAILABLE; char errBuf[16]; HMODULE hmod; // try to load RwsCliXX.dll; first, see if the RWS WPS class is // registered by f/q name - if so, look for RwsCli in the same // directory; the goal is to consistently use the same pair of // dlls if the user has multiple copies uint32_t rc = 1; // get the list of registered WPS classes ULONG cbClass; if (!WinEnumObjectClasses(nullptr, &cbClass)) return NS_ERROR_NOT_AVAILABLE; char *pBuf = (char*)NS_Alloc(cbClass + CCHMAXPATH); if (!pBuf) return NS_ERROR_OUT_OF_MEMORY; POBJCLASS pClass = (POBJCLASS)&pBuf[CCHMAXPATH]; if (!WinEnumObjectClasses(pClass, &cbClass)) { NS_Free(pBuf); return NS_ERROR_NOT_AVAILABLE; } // look for RWSxx while (pClass) { if (!strcmp(pClass->pszClassName, RWSCLASSNAME)) break; pClass = pClass->pNext; } // if the class was found & it was registered with a f/q name, // try to load RwsCliXX from the same directory if (pClass && pClass->pszModName[1] == ':') { strcpy(pBuf, pClass->pszModName); char *ptr = strrchr(pBuf, '\\'); if (ptr) { strcpy(ptr+1, RWSCLIDLL); rc = DosLoadModule(errBuf, sizeof(errBuf), pBuf, &hmod); } } NS_Free(pBuf); // if RwsCli couldn't be found, look for it on the LIBPATH if (rc) rc = DosLoadModule(errBuf, sizeof(errBuf), RWSCLIMOD, &hmod); // the dll couldn't be found, so exit if (rc) { ERRPRINTF(RWSCLIDLL, "nsRwsServiceInit - unable to locate %s"); return NS_ERROR_NOT_AVAILABLE; } // get the addresses of the procs we'll be using; if (DosQueryProcAddr(hmod, ORD_RWSCALL, 0, (PFN*)&sRwsCall) || DosQueryProcAddr(hmod, ORD_RWSCALLINDIRECT,0, (PFN*)&sRwsCallIndirect) || DosQueryProcAddr(hmod, ORD_RWSFREEMEM, 0, (PFN*)&sRwsFreeMem) || DosQueryProcAddr(hmod, ORD_RWSGETRESULT, 0, (PFN*)&sRwsGetResult) || DosQueryProcAddr(hmod, ORD_RWSGETARGPTR, 0, (PFN*)&sRwsGetArgPtr) || DosQueryProcAddr(hmod, ORD_RWSCLIENTINIT, 0, (PFN*)&sRwsClientInit) || DosQueryProcAddr(hmod, ORD_RWSGETTIMEOUT, 0, (PFN*)&sRwsGetTimeout) || DosQueryProcAddr(hmod, ORD_RWSSETTIMEOUT, 0, (PFN*)&sRwsSetTimeout) || DosQueryProcAddr(hmod, ORD_RWSCLIENTTERMINATE, 0, (PFN*)&sRwsClientTerminate)) { DosFreeModule(hmod); ERRPRINTF("", "nsRwsServiceInit - DosQueryProcAddr failed%s") return NS_ERROR_NOT_AVAILABLE; } // init RWS and have it register the WPS class if needed rc = sRwsClientInit(TRUE); if (rc) { ERRMSG(rc, "RwsClientInit") return NS_ERROR_NOT_AVAILABLE; } // if the user hasn't set a timeout, reset it to 2 seconds // (the default is 20 seconds) uint32_t currentTO; uint32_t userTO; rc = sRwsGetTimeout((PULONG)¤tTO, (PULONG)&userTO); if (rc) ERRMSG(rc, "RwsGetTimeout") else if (userTO == 0) { rc = sRwsSetTimeout(2); if (rc) ERRMSG(rc, "RwsSetTimeout") } // create an instance of nsRwsService sRwsInstance = new nsRwsService(); if (sRwsInstance == 0) return NS_ERROR_OUT_OF_MEMORY; *aClass = sRwsInstance; NS_ADDREF(*aClass); // set the class up as a shutdown observer nsCOMPtr os = mozilla::services::GetObserverService(); if (os) os->AddObserver(*aClass, "quit-application", false); return NS_OK; } //------------------------------------------------------------------------ // this is a variation on NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(); // the only difference is that the _GetterProc returns an nsresult to // provide a more appropriate error code (typically NS_ERROR_NOT_AVAILABLE) NS_IMETHODIMP nsRwsServiceConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult) { nsresult rv; nsRwsService *inst; *aResult = nullptr; if (aOuter) { rv = NS_ERROR_NO_AGGREGATION; return rv; } rv = nsRwsServiceInit(&inst); if (NS_FAILED(rv)) { ERRPRINTF(rv, "==>> nsRwsServiceInit failed - rv= %x"); return rv; } rv = inst->QueryInterface(aIID, aResult); NS_RELEASE(inst); return rv; } //------------------------------------------------------------------------